diff --git a/.github/workflows/build-prs.yml b/.github/workflows/build-prs.yml index 02ccfb147b..e8419119b1 100644 --- a/.github/workflows/build-prs.yml +++ b/.github/workflows/build-prs.yml @@ -26,6 +26,8 @@ jobs: uses: neoforged/actions/setup-java@main with: java-version: 21 + # Exclude minecraft sources from emitting annotation warnings since we cannot point to them anyway + warning-file-path: '(?!.+\/projects\/neoforge\/src\/)[^:]+' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/check-local-changes.yml b/.github/workflows/check-local-changes.yml index 8a9b325cf5..23890b53d3 100644 --- a/.github/workflows/check-local-changes.yml +++ b/.github/workflows/check-local-changes.yml @@ -19,6 +19,7 @@ jobs: uses: neoforged/actions/setup-java@main with: java-version: 21 + problem-matcher: false - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/test-prs.yml b/.github/workflows/test-prs.yml index c4d3b7e617..eac0691673 100644 --- a/.github/workflows/test-prs.yml +++ b/.github/workflows/test-prs.yml @@ -23,6 +23,7 @@ jobs: uses: neoforged/actions/setup-java@main with: java-version: 21 + problem-matcher: false - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch index ab7b5bd327..ed4cbaf831 100644 --- a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch @@ -18,11 +18,12 @@ private static boolean isFaceOccludedByState(Direction p_110980_, float p_110981_, BlockState p_110983_) { VoxelShape voxelshape = p_110983_.getFaceOcclusionShape(p_110980_.getOpposite()); if (voxelshape == Shapes.empty()) { -@@ -64,14 +_,20 @@ +@@ -64,14 +_,21 @@ return isFaceOccludedByState(p_110963_.getOpposite(), 1.0F, p_110962_); } + /** @deprecated Neo: use overload that accepts BlockState */ ++ @Deprecated public static boolean shouldRenderFace(FluidState p_203169_, BlockState p_203170_, Direction p_203171_, FluidState p_203172_) { return !isFaceOccludedBySelf(p_203170_, p_203171_) && !isNeighborSameFluid(p_203169_, p_203172_); } diff --git a/patches/net/minecraft/data/tags/EnchantmentTagsProvider.java.patch b/patches/net/minecraft/data/tags/EnchantmentTagsProvider.java.patch index fe6af81dab..97ecc18938 100644 --- a/patches/net/minecraft/data/tags/EnchantmentTagsProvider.java.patch +++ b/patches/net/minecraft/data/tags/EnchantmentTagsProvider.java.patch @@ -1,10 +1,11 @@ --- a/net/minecraft/data/tags/EnchantmentTagsProvider.java +++ b/net/minecraft/data/tags/EnchantmentTagsProvider.java -@@ -13,8 +_,12 @@ +@@ -13,8 +_,13 @@ import net.minecraft.world.item.enchantment.Enchantment; public abstract class EnchantmentTagsProvider extends KeyTagProvider { + /** @deprecated Forge: Use the {@linkplain #EnchantmentTagsProvider(PackOutput, CompletableFuture, String) mod id variant} */ ++ @Deprecated public EnchantmentTagsProvider(PackOutput p_341044_, CompletableFuture p_341146_) { super(p_341044_, Registries.ENCHANTMENT, p_341146_); + } diff --git a/patches/net/minecraft/data/tags/TagsProvider.java.patch b/patches/net/minecraft/data/tags/TagsProvider.java.patch index df473c00ea..3620aa8d8b 100644 --- a/patches/net/minecraft/data/tags/TagsProvider.java.patch +++ b/patches/net/minecraft/data/tags/TagsProvider.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/data/tags/TagsProvider.java +++ b/net/minecraft/data/tags/TagsProvider.java -@@ -32,26 +_,48 @@ +@@ -32,26 +_,49 @@ private final CompletableFuture> parentProvider; protected final ResourceKey> registryKey; protected final Map builders = Maps.newLinkedHashMap(); @@ -9,6 +9,7 @@ + /** + * @deprecated Forge: Use the {@linkplain #TagsProvider(PackOutput, ResourceKey, CompletableFuture, String) mod id variant} + */ ++ @Deprecated protected TagsProvider(PackOutput p_256596_, ResourceKey> p_255886_, CompletableFuture p_256513_) { - this(p_256596_, p_255886_, p_256513_, CompletableFuture.completedFuture(TagsProvider.TagLookup.empty())); + this(p_256596_, p_255886_, p_256513_, "vanilla"); diff --git a/patches/net/minecraft/gametest/framework/GameTestServer.java.patch b/patches/net/minecraft/gametest/framework/GameTestServer.java.patch index 43dd8a6ca1..8f079c6188 100644 --- a/patches/net/minecraft/gametest/framework/GameTestServer.java.patch +++ b/patches/net/minecraft/gametest/framework/GameTestServer.java.patch @@ -34,3 +34,33 @@ return true; } +@@ -216,8 +_,14 @@ + GlobalTestReporter.finish(); + LOGGER.info("========= {} GAME TESTS COMPLETE IN {} ======================", this.testTracker.getTotalCount(), this.stopwatch.stop()); + if (this.testTracker.hasFailedRequired()) { +- LOGGER.info("{} required tests failed :(", this.testTracker.getFailedRequiredCount()); ++ LOGGER.error("{} required tests failed :(", this.testTracker.getFailedRequiredCount()); + this.testTracker.getFailedRequired().forEach(GameTestServer::logFailedTest); ++ ++ // Neo: when running in GitHub actions emit actions-specific error annotations to make finding the error message easier ++ // See https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error ++ if (System.getenv().getOrDefault("CI", "false").equals("true") && System.getenv().getOrDefault("GITHUB_ACTIONS", "false").equals("true")) { ++ System.out.printf("\n::error title=GameTest Failure::%s required game tests failed: %s\n\n", testTracker.getFailedRequiredCount(), testTracker.getFailedRequired().stream().map(info -> info.id().toString()).collect(java.util.stream.Collectors.joining(", "))); ++ } + } else { + LOGGER.info("All {} required tests passed :)", this.testTracker.getTotalCount()); + } +@@ -233,11 +_,11 @@ + + private static void logFailedTest(GameTestInfo p_401161_) { + if (p_401161_.getRotation() != Rotation.NONE) { +- LOGGER.info( ++ LOGGER.error( + " - {} with rotation {}: {}", p_401161_.id(), p_401161_.getRotation().getSerializedName(), p_401161_.getError().getDescription().getString() + ); + } else { +- LOGGER.info(" - {}: {}", p_401161_.id(), p_401161_.getError().getDescription().getString()); ++ LOGGER.error(" - {}: {}", p_401161_.id(), p_401161_.getError().getDescription().getString()); + } + } + diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeAdvancementProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeAdvancementProvider.java index 6505d499ff..a6799846f7 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeAdvancementProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeAdvancementProvider.java @@ -334,6 +334,7 @@ public class NeoForgeAdvancementProvider extends AdvancementProvider { } @Nullable + @SuppressWarnings("removal") private Advancement.Builder findAndReplaceInHolder(AdvancementHolder advancementHolder, HolderLookup.Provider registries) { Advancement advancement = advancementHolder.value(); Advancement.Builder builder = Advancement.Builder.advancement(); diff --git a/testframework/src/main/java/net/neoforged/testframework/Test.java b/testframework/src/main/java/net/neoforged/testframework/Test.java index a3b92b3b98..5959690503 100644 --- a/testframework/src/main/java/net/neoforged/testframework/Test.java +++ b/testframework/src/main/java/net/neoforged/testframework/Test.java @@ -36,7 +36,9 @@ public interface Test extends Groupable { /** * A list of the groups of this test.
- * If this list is empty, the test will be only in the {@code ungrouped} group. + * If this list is empty, the test will be put in the {@code ungrouped} group. + *

+ * Tests without a {@link #asGameTest() game test} will also be automatically put in the {@code manual} group. * * @return the groups of this test */ @@ -197,10 +199,15 @@ public interface Test extends Groupable { /** * Represents the status of a test. * - * @param result the result - * @param message the message, providing additional context if the test failed + * @param result the result + * @param message the message, providing additional context if the test failed + * @param exception the exception with which the test failed. Can be {@code null} if the test did not fail or if it failed without throwing an exception */ - record Status(Result result, String message) { + record Status(Result result, String message, @Nullable Exception exception) { + public Status(Result result, String message) { + this(result, message, null); + } + public static final Status DEFAULT = new Status(Result.NOT_PROCESSED, ""); public static final Status PASSED = new Status(Result.PASSED, ""); @@ -213,7 +220,11 @@ public interface Test extends Groupable { } public static Status failed(String message) { - return new Status(Result.FAILED, message); + return failed(message, null); + } + + public static Status failed(String message, @Nullable Exception exception) { + return new Status(Result.FAILED, message, exception); } public MutableComponent asComponent() { @@ -230,7 +241,7 @@ public interface Test extends Groupable { if (message.isBlank()) { return "[result=" + result + "]"; } else { - return "[result=" + result + ",message=" + message + "]"; + return "[result=" + result + ",message=" + message + ",exception=" + exception + "]"; } } } diff --git a/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java b/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java index 2af9e437ce..7957b20d96 100644 --- a/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java +++ b/testframework/src/main/java/net/neoforged/testframework/client/AbstractTestScreen.java @@ -42,6 +42,7 @@ import org.lwjgl.glfw.GLFW; @ParametersAreNonnullByDefault public abstract class AbstractTestScreen extends Screen { protected final MutableTestFramework framework; + private final Screen outer = this; public AbstractTestScreen(Component title, MutableTestFramework framework) { super(title); @@ -186,7 +187,7 @@ public abstract class AbstractTestScreen extends Screen { graphics.blitSprite(RenderPipelines.GUI_TEXTURED, icon, pLeft, pTop, 9, 9, renderTransparent ? (alpha | 0x00FFFFFF) : 0xFFFFFFFF); final Component title = TestsOverlay.statusColoured(test.visuals().title(), status); - graphics.drawString(font, title, pLeft + 11, pTop, renderTransparent ? (alpha | 0xffffff0) : 0xffffff); + graphics.drawString(font, title, pLeft + 11, pTop, renderTransparent ? (alpha | 0x00FFFFFF) : 0xFFFFFFFF); } @Override @@ -266,9 +267,9 @@ public abstract class AbstractTestScreen extends Screen { final List all = group.resolveAll(); final int enabledCount = (int) all.stream().filter(it -> framework.tests().isEnabled(it.id())).count(); if (enabledCount == all.size()) { - graphics.setTooltipForNextFrame(font, Component.literal("All tests in group are enabled!").withStyle(ChatFormatting.GREEN), mouseX, mouseY); + graphics.setTooltipForNextFrame(font, Component.literal("All tests in group (" + all.size() + ") are enabled!").withStyle(ChatFormatting.GREEN), mouseX, mouseY); } else if (enabledCount == 0) { - graphics.setTooltipForNextFrame(font, Component.literal("All tests in group are disabled!").withStyle(ChatFormatting.GRAY), mouseX, mouseY); + graphics.setTooltipForNextFrame(font, Component.literal("All tests in group (" + all.size() + ") are disabled!").withStyle(ChatFormatting.GRAY), mouseX, mouseY); } else { graphics.setTooltipForNextFrame(font, Component.literal(enabledCount + "/" + all.size() + " tests enabled!").withStyle(ChatFormatting.BLUE), mouseX, mouseY); } @@ -291,7 +292,7 @@ public abstract class AbstractTestScreen extends Screen { } private void openBrowseGUI() { - Minecraft.getInstance().pushGuiLayer(new TestScreen( + Minecraft.getInstance().setScreen(new TestScreen( Component.literal("Tests of group ").append(getTitle()), framework, List.of(group)) { @Override @@ -302,7 +303,7 @@ public abstract class AbstractTestScreen extends Screen { showAsGroup.setValue(false); groupableList.resetRows(""); - addRenderableWidget(Button.builder(CommonComponents.GUI_BACK, (p_97691_) -> this.onClose()) + addRenderableWidget(Button.builder(CommonComponents.GUI_BACK, (p_97691_) -> minecraft.setScreen(outer)) .size(60, 20) .pos(this.width - 20 - 60, this.height - 29) .build()); diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/GameTestRegistration.java b/testframework/src/main/java/net/neoforged/testframework/impl/GameTestRegistration.java index 435865f8f1..4e298d533e 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/GameTestRegistration.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/GameTestRegistration.java @@ -70,7 +70,7 @@ public final class GameTestRegistration { @Override public void testFailed(GameTestInfo info, GameTestRunner runner) { - framework.changeStatus(test, Test.Status.failed("GameTest fail: " + info.getError().getMessage()), null); + framework.changeStatus(test, Test.Status.failed("GameTest failure: " + info.getError().getMessage(), info.getError()), null); disable(); } @@ -88,7 +88,7 @@ public final class GameTestRegistration { try { game.function().accept(helper); } catch (GameTestAssertException assertion) { - ((MutableTestFramework) framework).tests().setStatus(test.id(), Test.Status.failed("GameTest fail: " + assertion.getMessage())); + ((MutableTestFramework) framework).tests().setStatus(test.id(), Test.Status.failed("GameTest failure: " + assertion.getMessage(), assertion)); throw assertion; } } catch (GameTestAssertException exception) { diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java b/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java index 2363b0df07..12d06a1ea2 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -124,7 +125,7 @@ public class TestFrameworkImpl implements MutableTestFramework { boolean isGameTestRun = event.getServer() instanceof GameTestServer; // Summarise test results - var builder = new TestSummary.Builder(id(), isGameTestRun); + var builder = new TestSummary.Builder(this, isGameTestRun); tests().all().forEach(test -> { String id = test.id(); Test.Status status = tests().getStatus(id); @@ -316,7 +317,9 @@ public class TestFrameworkImpl implements MutableTestFramework { tests.globalListeners.forEach(listener -> listener.onStatusChange(this, test, oldStatus, newStatus, changer)); test.listeners().forEach(listener -> listener.onStatusChange(this, test, oldStatus, newStatus, changer)); - logger.info("Status of test '{}' has had status changed to {}{}.", test.id(), newStatus, changer instanceof Player player ? " by " + player.getGameProfile().getName() : ""); + BiConsumer logger = newStatus.result() == Test.Result.FAILED ? this.logger::error : this.logger::info; + + logger.accept("Test '{}' has had status changed to {}{}.", new Object[] { test.id(), newStatus, changer instanceof Player player ? " by " + player.getGameProfile().getName() : "" }); if (server == null && !inClientWorld) return; @@ -460,6 +463,10 @@ public class TestFrameworkImpl implements MutableTestFramework { test.groups().forEach(group -> getOrCreateGroup(group).add(test)); } test.init(TestFrameworkImpl.this); + + if (test.asGameTest() == null) { + getOrCreateGroup("manual").add(test); + } } private Group addGroupToParents(Group group) { diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/test/AbstractTest.java b/testframework/src/main/java/net/neoforged/testframework/impl/test/AbstractTest.java index fa2a05f720..0663e31447 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/test/AbstractTest.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/test/AbstractTest.java @@ -351,6 +351,11 @@ public abstract class AbstractTest implements Test { public void pass() { DynamicTest.super.pass(); } + + @Nullable + public Method getMethod() { + return null; + } } protected interface AnnotationHolder { diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedEventTest.java b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedEventTest.java index 4bbf03a39c..857f563d30 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedEventTest.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedEventTest.java @@ -13,6 +13,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.testframework.Test; import net.neoforged.testframework.impl.ReflectionUtils; +import org.jetbrains.annotations.Nullable; public class MethodBasedEventTest extends AbstractTest.Dynamic { protected MethodHandle handle; @@ -59,4 +60,10 @@ public class MethodBasedEventTest extends AbstractTest.Dynamic { } }); } + + @Nullable + @Override + public Method getMethod() { + return method; + } } diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedGameTestTest.java b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedGameTestTest.java index 3f2cd75dda..d752b1088d 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedGameTestTest.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedGameTestTest.java @@ -14,6 +14,7 @@ import net.neoforged.testframework.TestFramework; import net.neoforged.testframework.gametest.EmptyTemplate; import net.neoforged.testframework.gametest.GameTest; import net.neoforged.testframework.impl.ReflectionUtils; +import org.jetbrains.annotations.Nullable; public class MethodBasedGameTestTest extends AbstractTest.Dynamic { protected MethodHandle handle; @@ -46,4 +47,10 @@ public class MethodBasedGameTestTest extends AbstractTest.Dynamic { } }); } + + @Nullable + @Override + public Method getMethod() { + return method; + } } diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedTest.java b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedTest.java index 8df780e26c..5697f79005 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedTest.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/test/MethodBasedTest.java @@ -11,6 +11,7 @@ import net.neoforged.testframework.TestFramework; import net.neoforged.testframework.gametest.EmptyTemplate; import net.neoforged.testframework.gametest.GameTest; import net.neoforged.testframework.impl.ReflectionUtils; +import org.jetbrains.annotations.Nullable; public class MethodBasedTest extends AbstractTest.Dynamic { protected MethodHandle handle; @@ -40,4 +41,10 @@ public class MethodBasedTest extends AbstractTest.Dynamic { throw new RuntimeException("Encountered exception initiating method-based test: " + method, e); } } + + @Nullable + @Override + public Method getMethod() { + return method; + } } diff --git a/testframework/src/main/java/net/neoforged/testframework/summary/FileSummaryDumper.java b/testframework/src/main/java/net/neoforged/testframework/summary/FileSummaryDumper.java index 8d5613a546..6999f029b9 100644 --- a/testframework/src/main/java/net/neoforged/testframework/summary/FileSummaryDumper.java +++ b/testframework/src/main/java/net/neoforged/testframework/summary/FileSummaryDumper.java @@ -18,7 +18,7 @@ public interface FileSummaryDumper extends SummaryDumper { default void dump(TestSummary summary, Logger logger) { logger.info("Test summary processing..."); - Path outputPath = outputPath(summary.frameworkId()); + Path outputPath = outputPath(summary.framework().id()); try { Files.createDirectories(outputPath.getParent()); try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputPath))) { diff --git a/testframework/src/main/java/net/neoforged/testframework/summary/GitHubActionsStepSummaryDumper.java b/testframework/src/main/java/net/neoforged/testframework/summary/GitHubActionsStepSummaryDumper.java index 9b176e705c..df1c933b05 100644 --- a/testframework/src/main/java/net/neoforged/testframework/summary/GitHubActionsStepSummaryDumper.java +++ b/testframework/src/main/java/net/neoforged/testframework/summary/GitHubActionsStepSummaryDumper.java @@ -6,19 +6,35 @@ package net.neoforged.testframework.summary; import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; import net.minecraft.resources.ResourceLocation; import net.neoforged.testframework.Test; +import net.neoforged.testframework.impl.test.AbstractTest; import net.neoforged.testframework.summary.md.Alignment; import net.neoforged.testframework.summary.md.Table; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.slf4j.Logger; public class GitHubActionsStepSummaryDumper implements FileSummaryDumper { + private static final String SOURCE_FILE_ROOTS_PROPERTY = "net.neoforged.testframework.sourceFileRoots"; + private final Function heading; public GitHubActionsStepSummaryDumper() { @@ -77,6 +93,108 @@ public class GitHubActionsStepSummaryDumper implements FileSummaryDumper { } writer.println(); writer.println(builder.build()); + + // Generate check run annotations for failed tests that are @TestHolder methods + if (!failedTests.isEmpty() && System.getProperty(SOURCE_FILE_ROOTS_PROPERTY) != null) { + var roots = Arrays.stream(System.getProperty(SOURCE_FILE_ROOTS_PROPERTY).split(",")).map(Path::of).toList(); + + record TestLocation(Path path, Method method, String message, int line) {} + List locations = new ArrayList<>(); + + for (var testInfo : failedTests) { + var test = summary.framework().tests().byId(testInfo.testId()).orElseThrow(); + if (!(test instanceof AbstractTest.Dynamic dynamic)) continue; + var method = dynamic.getMethod(); + if (method == null) continue; + + var declaring = method.getDeclaringClass(); + + // Try to find the method's class file and read its bytecode to figure out the name of the source file and the line number range of the test method + try (var is = declaring.getClassLoader().getResourceAsStream(declaring.getName().replace(".", "/") + ".class")) { + if (is == null) continue; + + AtomicReference source = new AtomicReference<>(); + // We collect both the first and the last line of the method to be able to find stack track elements included within the method's bounds + AtomicInteger firstLine = new AtomicInteger(-1), lastLine = new AtomicInteger(); + + var desc = Type.getMethodDescriptor(method); + new ClassReader(is).accept(new ClassVisitor(Opcodes.ASM9) { + @Override + public void visitSource(String s, String debug) { + source.set(s); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (source.get() != null && name.equals(method.getName()) && desc.equals(descriptor)) { + return new MethodVisitor(Opcodes.ASM9) { + private int lastFoundLine; + + @Override + public void visitLineNumber(int line, Label start) { + if (firstLine.get() == -1) { + firstLine.set(line); + } + lastFoundLine = line; + } + + @Override + public void visitEnd() { + lastLine.set(lastFoundLine); + } + }; + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + }, ClassReader.SKIP_FRAMES); + + // If we cannot find the method within the class file or if it doesn't have line number information we can't emit any annotation + if (firstLine.get() == -1) continue; + + var relativeClassPath = declaring.getPackageName().replace(".", "/") + "/" + source.get(); + + for (Path root : roots) { + + // Try to find the first source root folder where a source file with the name found in the bytecode and corresponding package exists + var possibleFile = root.resolve(relativeClassPath); + if (Files.exists(possibleFile)) { + int line = firstLine.get(); + + var exception = testInfo.status().exception(); + if (exception != null) { + // If we have an exception, try to point the annotation at the first line of the exception within the same source file + // and within the lines of the method, otherwise, we point it at the first line of the method test that failed + for (StackTraceElement element : exception.getStackTrace()) { + if (Objects.equals(element.getFileName(), source.get()) && firstLine.get() <= element.getLineNumber() && element.getLineNumber() <= lastLine.get()) { + line = element.getLineNumber(); + break; + } + } + } + + locations.add(new TestLocation(possibleFile.toAbsolutePath(), method, testInfo.message(), line)); + break; + } + } + } catch (Exception ex) { + logger.error("Failed to read class declaring method {}", method, ex); + } + } + + if (!locations.isEmpty()) { + // Finally, emit the annotations but make sure to first relativise all paths to the workspace folder + var workspace = Path.of(System.getenv("GITHUB_WORKSPACE")).toAbsolutePath(); + var errorMessage = locations.stream() + .map(loc -> "::error file=" + workspace.relativize(loc.path()) + + ",line=" + loc.line() + ",title=Test " + loc.method() + " failed::" + loc.message()) + .collect(Collectors.joining("\n")); + // Print an empty line before to flush any dangling ANSI modifiers + System.out.println(); + System.out.println(errorMessage); + // And an empty line after for symmetry + System.out.println(); + } + } } protected String formatStatus(Test.Result result, boolean optional) { diff --git a/testframework/src/main/java/net/neoforged/testframework/summary/JUnitSummaryDumper.java b/testframework/src/main/java/net/neoforged/testframework/summary/JUnitSummaryDumper.java index be2263ade7..56863438dd 100644 --- a/testframework/src/main/java/net/neoforged/testframework/summary/JUnitSummaryDumper.java +++ b/testframework/src/main/java/net/neoforged/testframework/summary/JUnitSummaryDumper.java @@ -59,7 +59,7 @@ public class JUnitSummaryDumper implements FileSummaryDumper { DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder(); Document document = documentBuilder.newDocument(); Element testsuites = document.createElement("testsuites"); - testsuites.setAttribute("name", summary.frameworkId().toString()); + testsuites.setAttribute("name", summary.framework().id().toString()); testsuites.setAttribute("tests", Integer.toString(root.tests)); testsuites.setAttribute("failures", Integer.toString(root.failures)); testsuites.setAttribute("skipped", Integer.toString(root.skipped)); diff --git a/testframework/src/main/java/net/neoforged/testframework/summary/TestSummary.java b/testframework/src/main/java/net/neoforged/testframework/summary/TestSummary.java index f31ae3f7b9..e64f1160c6 100644 --- a/testframework/src/main/java/net/neoforged/testframework/summary/TestSummary.java +++ b/testframework/src/main/java/net/neoforged/testframework/summary/TestSummary.java @@ -8,10 +8,10 @@ package net.neoforged.testframework.summary; import com.google.common.collect.ImmutableList; import java.util.List; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; import net.neoforged.testframework.Test; +import net.neoforged.testframework.TestFramework; -public record TestSummary(ResourceLocation frameworkId, boolean isGameTestRun, List testInfos) { +public record TestSummary(TestFramework framework, boolean isGameTestRun, List testInfos) { public record TestInfo( String testId, Component name, @@ -31,12 +31,12 @@ public record TestSummary(ResourceLocation frameworkId, boolean isGameTestRun, L } public static class Builder { - private final ResourceLocation frameworkId; + private final TestFramework framework; private final boolean isGameTestRun; private final ImmutableList.Builder tests = ImmutableList.builder(); - public Builder(ResourceLocation frameworkId, boolean isGameTestRun) { - this.frameworkId = frameworkId; + public Builder(TestFramework framework, boolean isGameTestRun) { + this.framework = framework; this.isGameTestRun = isGameTestRun; } @@ -45,7 +45,7 @@ public record TestSummary(ResourceLocation frameworkId, boolean isGameTestRun, L } public TestSummary build() { - return new TestSummary(frameworkId, isGameTestRun, tests.build()); + return new TestSummary(framework, isGameTestRun, tests.build()); } } } diff --git a/tests/build.gradle b/tests/build.gradle index 943aee1b34..63fc77daa7 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -82,6 +82,9 @@ neoDev { configureEach { gameDirectory = layout.projectDir.dir("run/$name") systemProperty("terminal.ansi", "true") + + // This property allows the test framework to find source files and correctly annotate test failures in action runs + systemProperty('net.neoforged.testframework.sourceFileRoots', project.file('src/main/java').toPath().toAbsolutePath().toString()) } client { client() diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/DimensionTransitionScreenTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/DimensionTransitionScreenTests.java index 1eef53034d..27c2a655cc 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/DimensionTransitionScreenTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/DimensionTransitionScreenTests.java @@ -21,7 +21,7 @@ import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.gametest.EmptyTemplate; -@ForEachTest(side = Dist.CLIENT, groups = { DimensionTransitionScreenTests.GROUP, "manual" }) +@ForEachTest(side = Dist.CLIENT, groups = DimensionTransitionScreenTests.GROUP) public class DimensionTransitionScreenTests { public static final String GROUP = "dimension_transition"; public static final ResourceLocation NETHER_BG = ResourceLocation.withDefaultNamespace("textures/block/netherrack.png"); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java index 17aa528588..791f303fa4 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/MapDecorationRenderTests.java @@ -32,14 +32,12 @@ import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; -import net.neoforged.testframework.gametest.EmptyTemplate; import org.joml.Matrix4f; @ForEachTest(side = Dist.CLIENT, groups = MapDecorationRenderTests.GROUP) public class MapDecorationRenderTests { public static final String GROUP = "map_decoration_render"; - @EmptyTemplate @TestHolder(description = "Tests if custom map decoration renderers work", enabledByDefault = true) static void customRenderer(DynamicTest test) { var decorationType = test.registrationHelper().registrar(Registries.MAP_DECORATION_TYPE).register( @@ -70,7 +68,6 @@ public class MapDecorationRenderTests { }); } - @EmptyTemplate @TestHolder(description = "Tests if custom map decoration render state data works") static void customRenderData(DynamicTest test) { var key = new ContextKey(ResourceLocation.fromNamespaceAndPath(test.createModId(), "custom_color")); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java index 988ed7c910..a917fb6067 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java @@ -23,8 +23,6 @@ import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; -import net.neoforged.testframework.gametest.EmptyTemplate; -import net.neoforged.testframework.gametest.GameTest; @ForEachTest(side = Dist.CLIENT, groups = { "client.texture_atlas", "texture_atlas" }) public class TextureAtlasTests { @@ -69,8 +67,6 @@ public class TextureAtlasTests { } @TestHolder(description = { "Tests that custom sprite metadata sections get passed through resource reloading properly" }, enabledByDefault = true) - @GameTest - @EmptyTemplate static void defaultSpriteMetadataSections(final DynamicTest test) { String modId = test.createModId(); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java index e226e739b1..7f05cfb93f 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java @@ -10,6 +10,7 @@ import net.minecraft.core.registries.Registries; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.data.recipes.RecipeOutput; import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.gametest.framework.GameTestServer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -85,6 +86,8 @@ public class CustomFeatureFlagsTests { .registerSimpleItem("ext_range_disabled_test", new Item.Properties().requiredFeatures(extRangeDisabledTestFlag)); test.eventListeners().forge().addListener((ServerStartedEvent event) -> { + if (event.getServer() instanceof GameTestServer) return; // The gametest server enables all flags, so we're not interested in running the check + FeatureFlagSet flagSet = event.getServer().getLevel(Level.OVERWORLD).enabledFeatures(); if (!baseRangeEnabledTestItem.get().isEnabled(flagSet)) { test.fail("Item with enabled custom flag in base mask range was unexpectedly disabled"); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java index 5eb5816a17..984bdaa29e 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/registries/DatapackEntryTests.java @@ -27,7 +27,7 @@ public class DatapackEntryTests { @GameTest @EmptyTemplate - @TestHolder(description = "Tests that datapack entry conditions are generated correctly", enabledByDefault = true) + @TestHolder(description = "Tests that datapack entry conditions are generated correctly") static void conditionalDatapackEntries(final DynamicTest test, final RegistrationHelper reg) { ResourceKey CONDITIONAL_FALSE_DAMAGE_TYPE = ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(reg.modId(), "conditional_false")); ResourceKey CONDITIONAL_TRUE_DAMAGE_TYPE = ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(reg.modId(), "conditional_true")); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityTests.java index edeb7abd5e..fdb1867ffd 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityTests.java @@ -6,6 +6,7 @@ package net.neoforged.neoforge.debug.entity; import java.util.function.Consumer; +import java.util.function.Supplier; import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; @@ -20,6 +21,8 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.storage.ValueInput; @@ -42,12 +45,14 @@ public class EntityTests { @EmptyTemplate @TestHolder(description = "Tests if custom fence gates without wood types work, allowing for the use of the vanilla block for non-wooden gates") static void customSpawnLogic(final DynamicTest test, final RegistrationHelper reg) { + Supplier attr = () -> AttributeSupplier.builder() + .add(Attributes.MAX_HEALTH, 1); final var usingForgeAdvancedSpawn = reg.entityTypes().registerEntityType("complex_spawn", CustomComplexSpawnEntity::new, MobCategory.AMBIENT, builder -> builder.sized(1, 1)) - .withLang("Custom complex spawn egg").withRenderer(() -> NoopRenderer::new); + .withLang("Custom complex spawn egg").withAttributes(attr).withRenderer(() -> NoopRenderer::new); final var usingCustomPayloadsSpawn = reg.entityTypes().registerEntityType("adapted_spawn", AdaptedSpawnEntity::new, MobCategory.AMBIENT, builder -> builder.sized(1, 1)) - .withLang("Adapted complex spawn egg").withRenderer(() -> NoopRenderer::new); + .withLang("Adapted complex spawn egg").withAttributes(attr).withRenderer(() -> NoopRenderer::new); final var simpleSpawn = reg.entityTypes().registerEntityType("simple_spawn", SimpleEntity::new, MobCategory.AMBIENT, builder -> builder.sized(1, 1)) - .withLang("Simple spawn egg").withRenderer(() -> NoopRenderer::new); + .withLang("Simple spawn egg").withAttributes(attr).withRenderer(() -> NoopRenderer::new); reg.eventListeners().accept((Consumer) event -> event.registrar("1") .playToClient(EntityTests.CustomSyncPayload.TYPE, CustomSyncPayload.STREAM_CODEC, (payload, context) -> {})); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java index 3691eaa524..3507c26b86 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/AdvancementTests.java @@ -84,6 +84,7 @@ public class AdvancementTests { @GameTest @EmptyTemplate + @SuppressWarnings("removal") @TestHolder(description = "Tests if custom advancement predicates work") static void customPredicateTest(final DynamicTest test, final RegistrationHelper reg) { DataComponentPredicate.Type type = new DataComponentPredicate.Type<>(RecordCodecBuilder.create(g -> g.group( diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/fml/MultipleEntrypointsTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/fml/MultipleEntrypointsTest.java index 889a9de50a..29c6ace84b 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/fml/MultipleEntrypointsTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/fml/MultipleEntrypointsTest.java @@ -9,11 +9,13 @@ import java.util.concurrent.atomic.AtomicInteger; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.common.Mod; import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.gametest.EmptyTemplate; import net.neoforged.testframework.gametest.ExtendedGameTestHelper; import net.neoforged.testframework.gametest.GameTest; +@ForEachTest(groups = "fml") public class MultipleEntrypointsTest { private static final String MOD_ID = "multiple_entrypoints_test"; private static final AtomicInteger CLIENT_COUNTER = new AtomicInteger(); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java index 0db0d12b3c..6635e82df1 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/resources/ModDatapackTest.java @@ -28,6 +28,7 @@ import net.neoforged.testframework.annotation.TestHolder; public class ModDatapackTest { public static final String GROUP = "resources"; + @SuppressWarnings("removal") @TestHolder(description = "Tests that mod datapacks are loaded properly on initial load and reload", enabledByDefault = true) static void modDatapack(final DynamicTest test) { final ResourceLocation testAdvancement = ResourceLocation.fromNamespaceAndPath(test.createModId(), "recipes/misc/test_advancement"); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/resources/RichTranslationsTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/resources/RichTranslationsTest.java index 7345c74ace..094887f781 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/resources/RichTranslationsTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/resources/RichTranslationsTest.java @@ -22,9 +22,9 @@ import org.apache.commons.lang3.mutable.MutableBoolean; public class RichTranslationsTest { public static final String GROUP = "resources"; - @TestHolder(description = "Tests that rich translations work properly", enabledByDefault = true) @GameTest @EmptyTemplate("1x1x1") + @TestHolder(description = "Tests that rich translations work properly") static void richTranslations(final DynamicTest test) { test.onGameTest(helper -> { String arg = "Example argument"; diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java index 0812df2328..eaab43559c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java @@ -522,6 +522,7 @@ public class DataGeneratorTest { private static class Advancements implements AdvancementSubProvider { @Override + @SuppressWarnings("removal") public void generate(HolderLookup.Provider registries, Consumer saver) { var obtainDirt = Advancement.Builder.advancement() .display(Items.DIRT, diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredHolderTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredHolderTest.java deleted file mode 100644 index ca8684af25..0000000000 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DeferredHolderTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.oldtest; - -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.registries.DeferredHolder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Checks that {@link DeferredHolder} works correctly, specifically that get() functions immediately - * after construction, if registries are already populated. - */ -@Mod(DeferredHolderTest.MODID) -public class DeferredHolderTest { - static final String MODID = "deferred_holder_test"; - - private static final boolean ENABLED = true; - - private static final Logger LOGGER = LogManager.getLogger(); - - public DeferredHolderTest(IEventBus modBus) { - if (!ENABLED) return; - - modBus.addListener(this::commonSetup); - } - - public void commonSetup(FMLCommonSetupEvent event) { - LOGGER.info("Stone 1: {}", DeferredHolder.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("minecraft", "stone")).get()); - } -} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DuplicateOptionalTagTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DuplicateOptionalTagTest.java deleted file mode 100644 index 7f087907cd..0000000000 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DuplicateOptionalTagTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.oldtest; - -import com.google.common.collect.Sets; -import java.util.Set; -import java.util.stream.Collectors; -import net.minecraft.core.Holder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.server.ServerStartedEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Tests that the values for defaulted optional tags defined in multiple places are combined. - * - *

The optional tag defined by this mod is deliberately not defined in a data pack, to cause it to 'default' and - * trigger the behavior being tested.

- * - * @see MinecraftForge/MinecraftForge#7570 - */ -@Mod(DuplicateOptionalTagTest.MODID) -public class DuplicateOptionalTagTest { - private static final Logger LOGGER = LogManager.getLogger(); - - static final String MODID = "duplicate_optional_tag_test"; - private static final ResourceLocation TAG_NAME = ResourceLocation.fromNamespaceAndPath(MODID, "test_optional_tag"); - - private static final Set TAG_A_DEFAULTS = Set.of(Blocks.BEDROCK); - private static final Set TAG_B_DEFAULTS = Set.of(Blocks.WHITE_WOOL); - - private static final TagKey TAG_A = BlockTags.create(TAG_NAME); - private static final TagKey TAG_B = BlockTags.create(TAG_NAME); - - public DuplicateOptionalTagTest() { - NeoForge.EVENT_BUS.addListener(this::onServerStarted); - } - - private void onServerStarted(ServerStartedEvent event) { - Set tagAValues = BuiltInRegistries.BLOCK.get(TAG_A).map(tag -> tag.stream().map(Holder::value).collect(Collectors.toUnmodifiableSet())).orElse(TAG_A_DEFAULTS); - Set tagBValues = BuiltInRegistries.BLOCK.get(TAG_B).map(tag -> tag.stream().map(Holder::value).collect(Collectors.toUnmodifiableSet())).orElse(TAG_B_DEFAULTS); - - if (!tagAValues.equals(tagBValues)) { - LOGGER.error("Values of both optional tag instances are not the same: first instance: {}, second instance: {}", tagAValues, tagBValues); - return; - } - - final Set expected = Sets.union(TAG_A_DEFAULTS, TAG_B_DEFAULTS).stream().collect(Collectors.toUnmodifiableSet()); - if (!tagAValues.equals(expected)) { - IllegalStateException e = new IllegalStateException("Optional tag values do not match!"); - LOGGER.error("Values of the optional tag do not match the expected union of their defaults: expected {}, got {}", expected, tagAValues, e); - return; - } - - LOGGER.info("Optional tag instances match each other and the expected union of their defaults"); - } -} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/PermissionTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/PermissionTest.java index ee100f8bc9..41c68a2556 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/PermissionTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/PermissionTest.java @@ -23,7 +23,7 @@ import org.apache.logging.log4j.Logger; @Mod("permissiontest") public class PermissionTest { - private static final boolean ENABLED = true; + private static final boolean ENABLED = false; private static final Logger LOGGER = LogManager.getLogger(); private static final PermissionNode boolPerm = new PermissionNode<>("permissiontest", "test.blob", PermissionTypes.BOOLEAN, (player, playerUUID, context) -> true); diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/RegistryCodecTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/RegistryCodecTest.java deleted file mode 100644 index c6b7570592..0000000000 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/RegistryCodecTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.oldtest.misc; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * This test mod show a few example usages of {@link Registry#byNameCodec()} to serialize and deserialize registry entries to JSON or NBT. - * There are 4 tested cases : - * 1. json -> Pair - * 2. Pair -> nbt - * 3. Pair -> compressed json - * 4. compressed json -> Pair - * For each test the result will be logged. - */ -@Mod("registry_codec_test") -public class RegistryCodecTest { - private static final Logger LOGGER = LogManager.getLogger("Codec Registry Test"); - - /** - * This Codec can serialize and deserialize a {@code Pair}. - * The resulting JSON (or NBT equivalent) will have this structure: - * - *
{@code
-     * {
-     *     "block": "block_registry_name",
-     *     "item": "item_registry_name"
-     * }
-     * }
- */ - private static final Codec> CODEC = RecordCodecBuilder.create(codecInstance -> codecInstance.group( - BuiltInRegistries.BLOCK.byNameCodec().fieldOf("block").forGetter(Pair::getFirst), - BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(Pair::getSecond)).apply(codecInstance, Pair::of)); - - public RegistryCodecTest(IEventBus modEventBus) { - modEventBus.addListener(this::commonSetup); - } - - public void commonSetup(final FMLCommonSetupEvent event) { - //Create our Json to decode - JsonObject json = new JsonObject(); - json.addProperty("block", "minecraft:diamond_block"); - json.addProperty("item", "minecraft:diamond_pickaxe"); - - //Decode our Json and log an info in case of success or a warning in case of error - DataResult, JsonElement>> result = CODEC.decode(JsonOps.INSTANCE, json); - result.resultOrPartial(LOGGER::warn).ifPresent(pair -> LOGGER.info("Successfully decoded a diamond block and a diamond pickaxe from json to Block/Item")); - - //Create a Pair to test the serialization of our codec - Pair pair = Pair.of(Blocks.DIAMOND_BLOCK, Items.DIAMOND_PICKAXE); - - //Serialize the Pair to NBT, and log an info in case of success or a warning in case of error - DataResult result2 = CODEC.encodeStart(NbtOps.INSTANCE, pair); - result2.resultOrPartial(LOGGER::warn).ifPresent(tag -> LOGGER.info("Successfully encoded a Pair to a nbt tag: {}", tag)); - - //Serialize the Pair to JSON using the COMPRESSED JsonOps, this will use the int registry id instead of the ResourceLocation one, - //This is not recommended because int IDs can change, so you should not rely on them - DataResult result3 = CODEC.encodeStart(JsonOps.COMPRESSED, pair); - result3.resultOrPartial(LOGGER::warn).ifPresent(compressedJson -> LOGGER.info("Successfully encoded a Pair to a compressed json: {}", compressedJson)); - - //Create a json to decode using numerical IDs, to be decoded by JsonOps.COMPRESSED - JsonArray jsonCompressed = new JsonArray(); - jsonCompressed.add(BuiltInRegistries.BLOCK.getId(Blocks.DIAMOND_BLOCK)); - jsonCompressed.add(BuiltInRegistries.ITEM.getId(Items.DIAMOND_PICKAXE)); - - //Decode a compressed json to the corresponding Pair, this time using Codec#parse - DataResult> result4 = CODEC.parse(JsonOps.COMPRESSED, jsonCompressed); - result4.resultOrPartial(LOGGER::warn).ifPresent(pair2 -> LOGGER.info("Successfully decoded a diamond block and a diamond pickaxe from compressed json to Block/Item")); - } -} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java index 4bb8d97fc2..b12cc036f4 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/world/LoginPacketSplitTest.java @@ -74,7 +74,7 @@ import org.slf4j.Logger; public class LoginPacketSplitTest { public static final Logger LOG = LogUtils.getLogger(); public static final String MOD_ID = "login_packet_split_test"; - public static final boolean ENABLED = true; + public static final boolean ENABLED = false; private static final Gson GSON = new Gson(); public static final ResourceKey> BIG_DATA = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(MOD_ID, "big_data")); diff --git a/tests/src/main/resources/META-INF/neoforge.mods.toml b/tests/src/main/resources/META-INF/neoforge.mods.toml index 9922dcf885..f52bc1061e 100644 --- a/tests/src/main/resources/META-INF/neoforge.mods.toml +++ b/tests/src/main/resources/META-INF/neoforge.mods.toml @@ -175,18 +175,12 @@ modId="part_entity_test" [[mods]] modId="custom_armor_model_test" [[mods]] -modId="duplicate_optional_tag_test" -[[mods]] modId="custom_particle_type_test" [[mods]] modId="renderable_test" [[mods]] modId="ingredient_invalidation" [[mods]] -modId="registry_codec_test" -[[mods]] -modId="deferred_holder_test" -[[mods]] modId="gametest_test" [[mods]] modId="many_mob_effects_test"