diff --git a/build.gradle b/build.gradle index 5930f9c8..cab681b6 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,7 @@ allprojects { spotless { java { licenseHeaderFile(rootProject.file("HEADER")).yearSeparator(", ") + targetExclude 'src/test/java/net/fabricmc/mappingio/lib/**/*.java' } } @@ -183,8 +184,20 @@ jar { } } +tasks.register("generateTestMappings", JavaExec) { + dependsOn("testClasses") + classpath = sourceSets.test.runtimeClasspath + mainClass = 'net.fabricmc.mappingio.TestFileUpdater' +} + +tasks.register("updateTestMappings", Copy) { + dependsOn("generateTestMappings") + from 'build/resources/test/' + into 'src/test/resources/' +} + // A task to ensure that the version being released has not already been released. -task checkVersion { +tasks.register("checkVersion") { doFirst { def xml = new URL("https://maven.fabricmc.net/net/fabricmc/mapping-io/maven-metadata.xml").text def metadata = new XmlSlurper().parseText(xml) diff --git a/mapping-io-extras/src/test/java/net/fabricmc/mappingio/extras/MappingTreeRemapperTest.java b/mapping-io-extras/src/test/java/net/fabricmc/mappingio/extras/MappingTreeRemapperTest.java index af5107fb..d93b96b4 100644 --- a/mapping-io-extras/src/test/java/net/fabricmc/mappingio/extras/MappingTreeRemapperTest.java +++ b/mapping-io-extras/src/test/java/net/fabricmc/mappingio/extras/MappingTreeRemapperTest.java @@ -19,20 +19,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; + import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Type; import net.fabricmc.mappingio.TestHelper; import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; public class MappingTreeRemapperTest { private static MappingTree mappingTree; private static MappingTreeRemapper remapper; @BeforeAll - public static void setup() { - mappingTree = TestHelper.createTestTree(); + public static void setup() throws IOException { + mappingTree = TestHelper.acceptTestMappings(new MemoryMappingTree()); remapper = new MappingTreeRemapper(mappingTree, "source", "target"); } diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirReader.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirReader.java index a22682ec..ef42bf20 100644 --- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirReader.java @@ -48,6 +48,9 @@ public static void read(Path dir, MappingVisitor visitor) throws IOException { } public static void read(Path dir, String sourceNs, String targetNs, MappingVisitor visitor) throws IOException { + if (!Files.exists(dir)) throw new IOException("Directory does not exist: " + dir); + if (!Files.isDirectory(dir)) throw new IOException("Not a directory: " + dir); + Set flags = visitor.getFlags(); MappingVisitor parentVisitor = null; diff --git a/src/test/java/net/fabricmc/mappingio/TestFileUpdater.java b/src/test/java/net/fabricmc/mappingio/TestFileUpdater.java new file mode 100644 index 00000000..dc55b91a --- /dev/null +++ b/src/test/java/net/fabricmc/mappingio/TestFileUpdater.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio; + +import java.io.IOException; +import java.nio.file.Path; + +import net.fabricmc.mappingio.format.MappingFormat; + +public class TestFileUpdater { + public static void main(String[] args) throws IOException { + for (MappingFormat format : MappingFormat.values()) { + if (!format.hasWriter) { + continue; + } + + Path defaultPath = TestHelper.MappingDirs.VALID.resolve(TestHelper.getFileName(format)); + Path holesPath = TestHelper.MappingDirs.VALID_WITH_HOLES.resolve(TestHelper.getFileName(format)); + Path repeatPath = TestHelper.MappingDirs.REPEATED_ELEMENTS.resolve(TestHelper.getFileName(format)); + + TestHelper.acceptTestMappings(MappingWriter.create(defaultPath, format)); + TestHelper.acceptTestMappingsWithHoles(MappingWriter.create(holesPath, format)); + + if (format != MappingFormat.ENIGMA_DIR) { + TestHelper.acceptTestMappingsWithRepeats( + MappingWriter.create(repeatPath, format), + format != MappingFormat.ENIGMA_FILE, + format != MappingFormat.ENIGMA_FILE); + } + } + } +} diff --git a/src/test/java/net/fabricmc/mappingio/TestHelper.java b/src/test/java/net/fabricmc/mappingio/TestHelper.java index d6f1a5dd..7411a35d 100644 --- a/src/test/java/net/fabricmc/mappingio/TestHelper.java +++ b/src/test/java/net/fabricmc/mappingio/TestHelper.java @@ -20,6 +20,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -28,8 +29,10 @@ import org.cadixdev.lorenz.io.MappingFormats; import org.jetbrains.annotations.Nullable; +import net.fabricmc.mappingio.adapter.ForwardingMappingVisitor; import net.fabricmc.mappingio.format.MappingFormat; import net.fabricmc.mappingio.format.intellij.MigrationMapConstants; +import net.fabricmc.mappingio.lib.jool.Unchecked; import net.fabricmc.mappingio.tree.MappingTreeView; import net.fabricmc.mappingio.tree.MemoryMappingTree; @@ -144,159 +147,325 @@ public static Path writeToDir(MappingTreeView tree, Path dir, MappingFormat form return path; } - // Has to be kept in sync with /resources/read/valid/* test mappings! - public static MemoryMappingTree createTestTree() { - MemoryMappingTree tree = new MemoryMappingTree(); - tree.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_TARGET_FALLBACK + "2")); - tree.visitMetadata("name", "valid"); - tree.visitMetadata(MigrationMapConstants.ORDER_KEY, "0"); - int[] dstNs = new int[] { 0, 1 }; - nameGen.reset(); - - visitClass(tree, dstNs); - visitField(tree, dstNs); - visitMethod(tree, dstNs); - visitMethodArg(tree, dstNs); - visitMethodVar(tree, dstNs); - visitInnerClass(tree, 1, dstNs); - visitComment(tree); - visitField(tree, dstNs); - visitClass(tree, dstNs); - - return tree; - } + // After any changes, run "./gradlew updateTestMappings" to update the mapping files in the resources folder accordingly + public static T acceptTestMappings(T target) throws IOException { + MappingVisitor delegate = target instanceof VisitOrderVerifyingVisitor ? target : new VisitOrderVerifyingVisitor(target); + + if (delegate.visitHeader()) { + delegate.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_TARGET_FALLBACK + "2")); + delegate.visitMetadata("name", "valid"); + delegate.visitMetadata(MigrationMapConstants.ORDER_KEY, "0"); + } - // Has to be kept in sync with /resources/read/valid-with-holes/* test mappings! - public static MemoryMappingTree createTestTreeWithHoles() { - MemoryMappingTree tree = new MemoryMappingTree(); - tree.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_TARGET_FALLBACK + "2")); - nameGen.reset(); + if (delegate.visitContent()) { + int[] dstNs = new int[] { 0, 1 }; + nameGen.reset(); - // (Inner) Classes - for (int nestLevel = 0; nestLevel <= 2; nestLevel++) { - visitClass(tree); - visitInnerClass(tree, nestLevel, 0); - visitInnerClass(tree, nestLevel, 1); + if (visitClass(delegate, dstNs)) { + visitField(delegate, dstNs); - visitInnerClass(tree, nestLevel); - visitComment(tree); + if (visitMethod(delegate, dstNs)) { + visitMethodArg(delegate, dstNs); + visitMethodVar(delegate, dstNs); + } + } - visitInnerClass(tree, nestLevel, 0); - visitComment(tree); + if (visitInnerClass(delegate, 1, dstNs)) { + visitComment(delegate); + visitField(delegate, dstNs); + } + + visitClass(delegate, dstNs); + } - visitInnerClass(tree, nestLevel, 1); - visitComment(tree); + if (!delegate.visitEnd()) { + acceptTestMappings(delegate); } - // Fields - visitClass(tree); - visitField(tree); - visitField(tree, 0); - visitField(tree, 1); + return target; + } + + // After any changes, run "./gradlew updateTestMappings" to update the mapping files in the resources folder accordingly. + // Make sure to keep the few manual changes in the files. + public static T acceptTestMappingsWithRepeats(T target, boolean repeatComments, boolean repeatClasses) throws IOException { + acceptTestMappings(new ForwardingMappingVisitor(new VisitOrderVerifyingVisitor(target, true)) { + private List replayQueue = new ArrayList<>(); + + @Override + public void visitMetadata(String key, @Nullable String value) throws IOException { + super.visitMetadata(key, key.equals("name") ? "repeated-elements" : value); + } + + @Override + public boolean visitClass(String srcName) throws IOException { + replayQueue.clear(); + + if (repeatClasses) { + replayQueue.add(Unchecked.runnable(() -> super.visitClass(srcName))); + } + + return super.visitClass(srcName); + } + + @Override + public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException { + replayQueue.clear(); + replayQueue.add(Unchecked.runnable(() -> super.visitField(srcName, srcDesc))); + return super.visitField(srcName, srcDesc); + } + + @Override + public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException { + replayQueue.clear(); + replayQueue.add(Unchecked.runnable(() -> super.visitMethod(srcName, srcDesc))); + return super.visitMethod(srcName, srcDesc); + } + + @Override + public boolean visitMethodArg(int lvIndex, int argIndex, String srcName) throws IOException { + replayQueue.clear(); + replayQueue.add(Unchecked.runnable(() -> super.visitMethodArg(lvIndex, argIndex, srcName))); + return super.visitMethodArg(lvIndex, argIndex, srcName); + } + + @Override + public boolean visitMethodVar(int lvIndex, int varIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException { + replayQueue.clear(); + replayQueue.add(Unchecked.runnable(() -> super.visitMethodVar(lvIndex, varIndex, startOpIdx, endOpIdx, srcName))); + return super.visitMethodVar(lvIndex, varIndex, startOpIdx, endOpIdx, srcName); + } + + @Override + public void visitDstName(MappedElementKind targetKind, int namespace, String name) throws IOException { + if (targetKind == MappedElementKind.CLASS && !repeatClasses) { + super.visitDstName(targetKind, namespace, name); + } else { + replayQueue.add(Unchecked.runnable(() -> super.visitDstName(targetKind, namespace, name))); + super.visitDstName(targetKind, namespace, name + "0"); + } + } + + @Override + public void visitDstDesc(MappedElementKind targetKind, int namespace, String desc) throws IOException { + replayQueue.add(Unchecked.runnable(() -> super.visitDstDesc(targetKind, namespace, desc))); + super.visitDstDesc(targetKind, namespace, desc); + } - visitField(tree); - visitComment(tree); + @Override + public boolean visitElementContent(MappedElementKind targetKind) throws IOException { + boolean ret = super.visitElementContent(targetKind); - visitField(tree, 0); - visitComment(tree); + if (!replayQueue.isEmpty()) { + replayQueue.forEach(Runnable::run); - visitField(tree, 1); - visitComment(tree); + ret = super.visitElementContent(targetKind); + } - // Methods - visitMethod(tree); - visitMethod(tree, 0); - visitMethod(tree, 1); + return ret; + } - visitMethod(tree); - visitComment(tree); + @Override + public void visitComment(MappedElementKind targetKind, String comment) throws IOException { + if (repeatComments) { + super.visitComment(targetKind, comment + "."); + } - visitMethod(tree, 0); - visitComment(tree); + super.visitComment(targetKind, comment); + } + }); - visitMethod(tree, 1); - visitComment(tree); + return target; + } - // Method args - visitMethod(tree); - visitMethodArg(tree); - visitMethodArg(tree, 1); - visitMethodArg(tree, 0); + // After any changes, run "./gradlew updateTestMappings" to update the mapping files in the resources folder accordingly + public static T acceptTestMappingsWithHoles(T target) throws IOException { + MappingVisitor delegate = target instanceof VisitOrderVerifyingVisitor ? target : new VisitOrderVerifyingVisitor(target); - visitMethodArg(tree); - visitComment(tree); + if (delegate.visitHeader()) { + delegate.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_TARGET_FALLBACK + "2")); + } - visitMethodArg(tree, 0); - visitComment(tree); + if (delegate.visitContent()) { + nameGen.reset(); - visitMethodArg(tree, 1); - visitComment(tree); + // (Inner) Classes + for (int nestLevel = 0; nestLevel <= 2; nestLevel++) { + visitClass(delegate); + visitInnerClass(delegate, nestLevel, 0); + visitInnerClass(delegate, nestLevel, 1); - // Method vars - visitMethod(tree); - visitMethodVar(tree); - visitMethodVar(tree, 1); - visitMethodVar(tree, 0); + if (visitInnerClass(delegate, nestLevel)) { + visitComment(delegate); + } - visitMethodVar(tree); - visitComment(tree); + if (visitInnerClass(delegate, nestLevel, 0)) { + visitComment(delegate); + } - visitMethodVar(tree, 0); - visitComment(tree); + if (visitInnerClass(delegate, nestLevel, 1)) { + visitComment(delegate); + } + } - visitMethodVar(tree, 1); - visitComment(tree); + if (visitClass(delegate)) { + // Fields + visitField(delegate); + visitField(delegate, 0); + visitField(delegate, 1); + + if (visitField(delegate)) { + visitComment(delegate); + } + + if (visitField(delegate, 0)) { + visitComment(delegate); + } + + if (visitField(delegate, 1)) { + visitComment(delegate); + } + + // Methods + visitMethod(delegate); + visitMethod(delegate, 0); + visitMethod(delegate, 1); + + if (visitMethod(delegate)) { + visitComment(delegate); + } + + if (visitMethod(delegate, 0)) { + visitComment(delegate); + } + + if (visitMethod(delegate, 1)) { + visitComment(delegate); + } + + // Method args + if (visitMethod(delegate)) { + visitMethodArg(delegate); + visitMethodArg(delegate, 1); + visitMethodArg(delegate, 0); + + if (visitMethodArg(delegate)) { + visitComment(delegate); + } + + if (visitMethodArg(delegate, 0)) { + visitComment(delegate); + } + + if (visitMethodArg(delegate, 1)) { + visitComment(delegate); + } + } + + // Method vars + if (visitMethod(delegate)) { + visitMethodVar(delegate); + visitMethodVar(delegate, 1); + visitMethodVar(delegate, 0); + + if (visitMethodVar(delegate)) { + visitComment(delegate); + } + + if (visitMethodVar(delegate, 0)) { + visitComment(delegate); + } + + if (visitMethodVar(delegate, 1)) { + visitComment(delegate); + } + } + } + } + + if (!delegate.visitEnd()) { + acceptTestMappingsWithHoles(delegate); + } - return tree; + return target; } - private static void visitClass(MemoryMappingTree tree, int... dstNs) { - visitInnerClass(tree, 0, dstNs); + private static boolean visitClass(MappingVisitor target, int... dstNs) throws IOException { + return visitInnerClass(target, 0, dstNs); } - private static void visitInnerClass(MemoryMappingTree tree, int nestLevel, int... dstNs) { - tree.visitClass(nestLevel <= 0 ? nameGen.src(clsKind) : nameGen.srcInnerCls(nestLevel)); + private static boolean visitInnerClass(MappingVisitor target, int nestLevel, int... dstNs) throws IOException { + if (!target.visitClass(nestLevel <= 0 ? nameGen.src(clsKind) : nameGen.srcInnerCls(nestLevel))) { + return false; + } for (int ns : dstNs) { - tree.visitDstName(clsKind, ns, nameGen.dst(clsKind, ns)); + target.visitDstName(clsKind, ns, nameGen.dst(clsKind, ns)); } + + return target.visitElementContent(clsKind); } - private static void visitField(MemoryMappingTree tree, int... dstNs) { - tree.visitField(nameGen.src(fldKind), nameGen.desc(fldKind)); + private static boolean visitField(MappingVisitor target, int... dstNs) throws IOException { + String desc; + + if (!target.visitField(nameGen.src(fldKind), desc = nameGen.desc(fldKind))) { + return false; + } for (int ns : dstNs) { - tree.visitDstName(fldKind, ns, nameGen.dst(fldKind, ns)); + target.visitDstName(fldKind, ns, nameGen.dst(fldKind, ns)); + target.visitDstDesc(fldKind, ns, desc); } + + return target.visitElementContent(fldKind); } - private static void visitMethod(MemoryMappingTree tree, int... dstNs) { - tree.visitMethod(nameGen.src(mthKind), nameGen.desc(mthKind)); + private static boolean visitMethod(MappingVisitor target, int... dstNs) throws IOException { + String desc; + + if (!target.visitMethod(nameGen.src(mthKind), desc = nameGen.desc(mthKind))) { + return false; + } for (int ns : dstNs) { - tree.visitDstName(mthKind, ns, nameGen.dst(mthKind, ns)); + target.visitDstName(mthKind, ns, nameGen.dst(mthKind, ns)); + target.visitDstDesc(mthKind, ns, desc); } + + return target.visitElementContent(mthKind); } - private static void visitMethodArg(MemoryMappingTree tree, int... dstNs) { - tree.visitMethodArg(nameGen.getCounter().getAndIncrement(), nameGen.getCounter().getAndIncrement(), nameGen.src(argKind)); + private static boolean visitMethodArg(MappingVisitor target, int... dstNs) throws IOException { + if (!target.visitMethodArg(nameGen.getCounter().getAndIncrement(), nameGen.getCounter().getAndIncrement(), nameGen.src(argKind))) { + return false; + } for (int ns : dstNs) { - tree.visitDstName(argKind, ns, nameGen.dst(argKind, ns)); + target.visitDstName(argKind, ns, nameGen.dst(argKind, ns)); } + + return target.visitElementContent(argKind); } - private static void visitMethodVar(MemoryMappingTree tree, int... dstNs) { - tree.visitMethodVar(nameGen.getCounter().get(), nameGen.getCounter().get(), - nameGen.getCounter().getAndIncrement(), nameGen.getCounter().getAndIncrement(), nameGen.src(varKind)); + private static boolean visitMethodVar(MappingVisitor target, int... dstNs) throws IOException { + if (!target.visitMethodVar( + nameGen.getCounter().get(), + nameGen.getCounter().get(), + nameGen.getCounter().getAndIncrement(), + nameGen.getCounter().getAndIncrement(), + nameGen.src(varKind))) { + return false; + } for (int ns : dstNs) { - tree.visitDstName(varKind, ns, nameGen.dst(varKind, ns)); + target.visitDstName(varKind, ns, nameGen.dst(varKind, ns)); } + + return target.visitElementContent(varKind); } - private static void visitComment(MemoryMappingTree tree) { - tree.visitComment(nameGen.lastKind.get(), comment); + private static void visitComment(MappingVisitor target) throws IOException { + target.visitComment(nameGen.lastKind.get(), comment); } private static class NameGen { @@ -445,16 +614,17 @@ private String getPrefix(MappedElementKind kind) { public static class MappingDirs { @Nullable - public static MemoryMappingTree getCorrespondingTree(Path dir) { - if (dir.equals(VALID)) return createTestTree(); - if (dir.equals(VALID_WITH_HOLES)) return createTestTreeWithHoles(); + public static MemoryMappingTree getCorrespondingTree(Path dir) throws IOException { + if (dir.equals(VALID)) return acceptTestMappings(new MemoryMappingTree()); + if (dir.equals(REPEATED_ELEMENTS)) return acceptTestMappingsWithRepeats(new MemoryMappingTree(), true, true); + if (dir.equals(VALID_WITH_HOLES)) return acceptTestMappingsWithHoles(new MemoryMappingTree()); return null; } public static final Path DETECTION = getResource("/detection/"); public static final Path VALID = getResource("/read/valid/"); - public static final Path VALID_WITH_HOLES = getResource("/read/valid-with-holes/"); public static final Path REPEATED_ELEMENTS = getResource("/read/repeated-elements/"); + public static final Path VALID_WITH_HOLES = getResource("/read/valid-with-holes/"); } private static final List fldDescs = Arrays.asList("I", "Lcls;", "Lpkg/cls;", "[I"); diff --git a/src/test/java/net/fabricmc/mappingio/VisitOrderVerifyingVisitor.java b/src/test/java/net/fabricmc/mappingio/VisitOrderVerifyingVisitor.java index 4092f250..cbdb6211 100644 --- a/src/test/java/net/fabricmc/mappingio/VisitOrderVerifyingVisitor.java +++ b/src/test/java/net/fabricmc/mappingio/VisitOrderVerifyingVisitor.java @@ -105,7 +105,7 @@ public void visitMetadata(String key, @Nullable String value) throws IOException @Override public boolean visitContent() throws IOException { - assertNamespacesVisited(); + assertHeaderVisited(); assertContentNotVisited(); visitedContent = true; diff --git a/src/test/java/net/fabricmc/mappingio/lib/jool/Unchecked.java b/src/test/java/net/fabricmc/mappingio/lib/jool/Unchecked.java new file mode 100644 index 00000000..0e5901bd --- /dev/null +++ b/src/test/java/net/fabricmc/mappingio/lib/jool/Unchecked.java @@ -0,0 +1,104 @@ +/* + * Copyright (c), Data Geekery GmbH, contact@datageekery.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.lib.jool; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.function.Consumer; + +import net.fabricmc.mappingio.lib.jool.fi.lang.CheckedRunnable; + +/** + * Improved interoperability between checked exceptions and Java 8. + * + *

Checked exceptions are one of Java's biggest flaws. Due to backwards-compatibility, we're inheriting all the checked + * exception trouble back from JDK 1.0. This becomes even more obvious when using lambda expressions, most of which are + * not allowed to throw checked exceptions. + * + *

This library tries to ease some pain and wraps / unwraps a variety of API elements from the JDK 8 to improve + * interoperability with checked exceptions. + * + * @author Lukas Eder + */ +public class Unchecked { + private Unchecked() { } + + /** + * A {@link Consumer} that wraps any {@link Throwable} in a {@link RuntimeException}. + */ + public static final Consumer THROWABLE_TO_RUNTIME_EXCEPTION = t -> { + if (t instanceof Error) { + throw (Error) t; + } + + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + + if (t instanceof IOException) { + throw new UncheckedIOException((IOException) t); + } + + // [#230] Clients will not expect needing to handle this. + if (t instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + + throw new RuntimeException(t); + }; + + /** + * Wrap a {@link CheckedRunnable} in a {@link Runnable}. + * + *

Example: + *


+	 * new Thread(Unchecked.runnable(() -> {
+	 *     throw new Exception("Cannot run this thread");
+	 * })).start();
+	 * 
+ */ + public static Runnable runnable(CheckedRunnable runnable) { + return runnable(runnable, THROWABLE_TO_RUNTIME_EXCEPTION); + } + + /** + * Wrap a {@link CheckedRunnable} in a {@link Runnable} with a custom handler for checked exceptions. + * + *

Example: + *


+	 * new Thread(Unchecked.runnable(
+	 *     () -> {
+	 *         throw new Exception("Cannot run this thread");
+	 *     },
+	 *     e -> {
+	 *         throw new IllegalStateException(e);
+	 *     }
+	 * )).start();
+	 * 
+ */ + public static Runnable runnable(CheckedRunnable runnable, Consumer handler) { + return () -> { + try { + runnable.run(); + } catch (Throwable e) { + handler.accept(e); + + throw new IllegalStateException("Exception handler must throw a RuntimeException", e); + } + }; + } +} diff --git a/src/test/java/net/fabricmc/mappingio/lib/jool/fi/lang/CheckedRunnable.java b/src/test/java/net/fabricmc/mappingio/lib/jool/fi/lang/CheckedRunnable.java new file mode 100644 index 00000000..5ebd7726 --- /dev/null +++ b/src/test/java/net/fabricmc/mappingio/lib/jool/fi/lang/CheckedRunnable.java @@ -0,0 +1,30 @@ +/* + * Copyright (c), Data Geekery GmbH, contact@datageekery.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.lib.jool.fi.lang; + +/** + * A {@link Runnable} that allows for checked exceptions. + * + * @author Lukas Eder + */ +@FunctionalInterface +public interface CheckedRunnable { + /** + * Run this runnable. + */ + void run() throws Throwable; +} diff --git a/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java b/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java index 577f0d02..27b1121f 100644 --- a/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java +++ b/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java @@ -19,7 +19,6 @@ import java.nio.file.Path; import org.jetbrains.annotations.Nullable; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import net.fabricmc.mappingio.MappingReader; @@ -29,23 +28,11 @@ import net.fabricmc.mappingio.adapter.FlatAsRegularMappingVisitor; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; import net.fabricmc.mappingio.format.MappingFormat; -import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MappingTreeView; import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.mappingio.tree.VisitableMappingTree; public class ValidContentReadTest { - private static MappingTree testTree; - private static MappingTree testTreeWithHoles; - private static MappingTree testTreeWithRepeatedElements; - - @BeforeAll - public static void setup() throws Exception { - testTree = TestHelper.createTestTree(); - testTreeWithHoles = TestHelper.createTestTreeWithHoles(); - testTreeWithRepeatedElements = testTree; - } - @Test public void enigmaFile() throws Exception { MappingFormat format = MappingFormat.ENIGMA_FILE; @@ -160,11 +147,12 @@ public void jobfFile() throws Exception { private void checkDefault(MappingFormat format) throws Exception { Path path = TestHelper.MappingDirs.VALID.resolve(TestHelper.getFileName(format)); + VisitableMappingTree referenceTree = TestHelper.acceptTestMappings(new MemoryMappingTree()); VisitableMappingTree tree = new MemoryMappingTree(); boolean allowConsecutiveDuplicateElementVisits = false; MappingReader.read(path, format, new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits)); - assertEqual(tree, format, testTree, allowConsecutiveDuplicateElementVisits); + assertEqual(tree, format, referenceTree, allowConsecutiveDuplicateElementVisits); tree = new MemoryMappingTree(); MappingReader.read(path, format, @@ -172,28 +160,20 @@ private void checkDefault(MappingFormat format) throws Exception { new VisitOrderVerifyingVisitor( new MappingSourceNsSwitch( new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits), - testTree.getSrcNamespace()), + referenceTree.getSrcNamespace()), allowConsecutiveDuplicateElementVisits), - testTree.getDstNamespaces().get(0))); - assertEqual(tree, format, testTree, allowConsecutiveDuplicateElementVisits); - } - - private void checkHoles(MappingFormat format) throws Exception { - Path path = TestHelper.MappingDirs.VALID_WITH_HOLES.resolve(TestHelper.getFileName(format)); - - VisitableMappingTree tree = new MemoryMappingTree(); - boolean allowConsecutiveDuplicateElementVisits = false; - - MappingReader.read(path, format, new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits)); - assertEqual(tree, format, testTreeWithHoles, allowConsecutiveDuplicateElementVisits); + referenceTree.getDstNamespaces().get(0))); + assertEqual(tree, format, referenceTree, allowConsecutiveDuplicateElementVisits); } private void checkRepeated(MappingFormat format, boolean allowConsecutiveDuplicateElementVisits) throws Exception { Path path = TestHelper.MappingDirs.REPEATED_ELEMENTS.resolve(TestHelper.getFileName(format)); + VisitableMappingTree referenceTree = TestHelper.acceptTestMappingsWithRepeats(new MemoryMappingTree(), true, true); VisitableMappingTree tree = new MemoryMappingTree(); + MappingReader.read(path, format, new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits)); - assertEqual(tree, format, testTreeWithRepeatedElements, allowConsecutiveDuplicateElementVisits); + assertEqual(tree, format, referenceTree, allowConsecutiveDuplicateElementVisits); tree = new MemoryMappingTree(); MappingReader.read(path, format, @@ -201,10 +181,21 @@ private void checkRepeated(MappingFormat format, boolean allowConsecutiveDuplica new VisitOrderVerifyingVisitor( new MappingSourceNsSwitch( new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits), - testTreeWithRepeatedElements.getSrcNamespace()), + referenceTree.getSrcNamespace()), allowConsecutiveDuplicateElementVisits), - testTreeWithRepeatedElements.getDstNamespaces().get(0))); - assertEqual(tree, format, testTreeWithRepeatedElements, allowConsecutiveDuplicateElementVisits); + referenceTree.getDstNamespaces().get(0))); + assertEqual(tree, format, referenceTree, allowConsecutiveDuplicateElementVisits); + } + + private void checkHoles(MappingFormat format) throws Exception { + Path path = TestHelper.MappingDirs.VALID_WITH_HOLES.resolve(TestHelper.getFileName(format)); + + VisitableMappingTree referenceTree = TestHelper.acceptTestMappingsWithHoles(new MemoryMappingTree()); + VisitableMappingTree tree = new MemoryMappingTree(); + boolean allowConsecutiveDuplicateElementVisits = false; + + MappingReader.read(path, format, new VisitOrderVerifyingVisitor(tree, allowConsecutiveDuplicateElementVisits)); + assertEqual(tree, format, referenceTree, allowConsecutiveDuplicateElementVisits); } private void assertEqual(MappingTreeView tree, MappingFormat format, MappingTreeView referenceTree, boolean allowConsecutiveDuplicateElementVisits) throws Exception { diff --git a/src/test/java/net/fabricmc/mappingio/tree/MetadataTest.java b/src/test/java/net/fabricmc/mappingio/tree/MetadataTest.java index cacab9bb..8965a20a 100644 --- a/src/test/java/net/fabricmc/mappingio/tree/MetadataTest.java +++ b/src/test/java/net/fabricmc/mappingio/tree/MetadataTest.java @@ -42,7 +42,7 @@ public class MetadataTest { @BeforeAll public static void setup() throws Exception { - tree = TestHelper.createTestTree(); + tree = TestHelper.acceptTestMappings(new MemoryMappingTree()); tree.getMetadata().clear(); for (int i = 0; i < 40; i++) { @@ -60,8 +60,8 @@ public void testOrder() throws Exception { tree.accept(new NopMappingVisitor(true) { @Override public void visitMetadata(String key, @Nullable String value) { - assertEquals(key, keys.get(visitCount)); - assertEquals(value, values.get(visitCount)); + assertEquals(keys.get(visitCount), key); + assertEquals(values.get(visitCount), value); visitCount++; } diff --git a/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java b/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java index d301f236..3574368c 100644 --- a/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java +++ b/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.EnumSet; import java.util.List; @@ -128,11 +129,14 @@ public void jobfFile() throws Exception { private void check(MappingFormat format) throws Exception { checkDir(TestHelper.MappingDirs.DETECTION, format); checkDir(TestHelper.MappingDirs.VALID, format); + checkDir(TestHelper.MappingDirs.REPEATED_ELEMENTS, format); checkDir(TestHelper.MappingDirs.VALID_WITH_HOLES, format); } private void checkDir(Path dir, MappingFormat format) throws Exception { Path path = dir.resolve(TestHelper.getFileName(format)); + if (!Files.exists(path)) return; + MappingTreeView supTree = TestHelper.MappingDirs.getCorrespondingTree(dir); checkCompliance(format, path, 1, true, supTree); diff --git a/src/test/java/net/fabricmc/mappingio/write/WriteTest.java b/src/test/java/net/fabricmc/mappingio/write/WriteTest.java index 7a0c1e7f..1da5f300 100644 --- a/src/test/java/net/fabricmc/mappingio/write/WriteTest.java +++ b/src/test/java/net/fabricmc/mappingio/write/WriteTest.java @@ -29,6 +29,7 @@ import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.MappingWriter; import net.fabricmc.mappingio.SubsetAssertingVisitor; import net.fabricmc.mappingio.TestHelper; import net.fabricmc.mappingio.adapter.FlatAsRegularMappingVisitor; @@ -44,16 +45,22 @@ public class WriteTest { private static Path dir; private static MappingTreeView validTree; private static Map treeNsAltMap = new HashMap<>(); + private static MappingTreeView validWithRepeatsTree; + private static Map treeWithRepeatsNsAltMap = new HashMap<>(); private static MappingTreeView validWithHolesTree; private static Map treeWithHolesNsAltMap = new HashMap<>(); @BeforeAll public static void setup() throws Exception { - validTree = TestHelper.createTestTree(); + validTree = TestHelper.acceptTestMappings(new MemoryMappingTree()); treeNsAltMap.put(validTree.getDstNamespaces().get(0), validTree.getSrcNamespace()); treeNsAltMap.put(validTree.getDstNamespaces().get(1), validTree.getSrcNamespace()); - validWithHolesTree = TestHelper.createTestTreeWithHoles(); + validWithRepeatsTree = TestHelper.acceptTestMappingsWithRepeats(new MemoryMappingTree(), true, true); + treeWithRepeatsNsAltMap.put(validWithRepeatsTree.getDstNamespaces().get(0), validWithRepeatsTree.getSrcNamespace()); + treeWithRepeatsNsAltMap.put(validWithRepeatsTree.getDstNamespaces().get(1), validWithRepeatsTree.getSrcNamespace()); + + validWithHolesTree = TestHelper.acceptTestMappingsWithHoles(new MemoryMappingTree()); treeWithHolesNsAltMap.put(validWithHolesTree.getDstNamespaces().get(0), validWithHolesTree.getSrcNamespace()); treeWithHolesNsAltMap.put(validWithHolesTree.getDstNamespaces().get(1), validWithHolesTree.getSrcNamespace()); } @@ -129,12 +136,19 @@ public void jobfFile() throws Exception { } private void check(MappingFormat format) throws Exception { - Path path = TestHelper.writeToDir(validTree, dir, format); + Path path = dir.resolve(TestHelper.getFileName(format)); + TestHelper.acceptTestMappings(MappingWriter.create(path, format)); readWithMio(validTree, path, format); readWithLorenz(path, format); readWithSrgUtils(validTree, format, treeNsAltMap); - path = TestHelper.writeToDir(validWithHolesTree, dir, format); + boolean isEnigma = format == MappingFormat.ENIGMA_FILE || format == MappingFormat.ENIGMA_DIR; + TestHelper.acceptTestMappingsWithRepeats(MappingWriter.create(path, format), !isEnigma, !isEnigma); + readWithMio(validWithRepeatsTree, path, format); + readWithLorenz(path, format); + readWithSrgUtils(validWithRepeatsTree, format, treeWithRepeatsNsAltMap); + + TestHelper.acceptTestMappingsWithHoles(MappingWriter.create(path, format)); readWithMio(validWithHolesTree, path, format); readWithLorenz(path, format); readWithSrgUtils(validWithHolesTree, format, treeWithHolesNsAltMap); diff --git a/src/test/resources/read/repeated-elements/enigma.mappings b/src/test/resources/read/repeated-elements/enigma.mappings index 5c888fb3..fb966451 100644 --- a/src/test/resources/read/repeated-elements/enigma.mappings +++ b/src/test/resources/read/repeated-elements/enigma.mappings @@ -9,6 +9,7 @@ CLASS class_1 class1Ns0Rename CLASS class_2 class2Ns0Rename0 CLASS class_2 class2Ns0Rename1 COMMENT This is a comment. + CLASS class_2 class2Ns0Rename2 CLASS class_2 class2Ns0Rename COMMENT This is a comment FIELD field_2 field2Ns0Rename0 Lcls; diff --git a/src/test/resources/read/valid-with-holes/xsrg.xsrg b/src/test/resources/read/valid-with-holes/xsrg.xsrg index 0cb4a99f..6145d47e 100644 --- a/src/test/resources/read/valid-with-holes/xsrg.xsrg +++ b/src/test/resources/read/valid-with-holes/xsrg.xsrg @@ -4,7 +4,7 @@ CL: class_7$class_8 class_7$class8Ns0Rename CL: class_13$class_14 class_13$class14Ns0Rename CL: class_17$class_18$class_19 class_17$class_18$class19Ns0Rename CL: class_26$class_27$class_28 class_26$class_27$class28Ns0Rename -FD: class_32/field_2 Lcls; class_32/field2Ns0Rename I +FD: class_32/field_2 Lcls; class_32/field2Ns0Rename Lcls; FD: class_32/field_5 I class_32/field5Ns0Rename I -MD: class_32/method_2 (I)V class_32/method2Ns0Rename ()I -MD: class_32/method_5 (Lcls;[I)[[B class_32/method5Ns0Rename ()I +MD: class_32/method_2 (I)V class_32/method2Ns0Rename (I)V +MD: class_32/method_5 (Lcls;[I)[[B class_32/method5Ns0Rename (Lcls;[I)[[B