diff --git a/io-mappingio/build.gradle b/io-mappingio/build.gradle index 847b8a1..6bfb4f8 100644 --- a/io-mappingio/build.gradle +++ b/io-mappingio/build.gradle @@ -6,6 +6,7 @@ dependencies { api libs.mappingio compileOnly libs.jetbrains.annotations + testCompileOnly libs.jetbrains.annotations testFixturesApi testFixtures(project(':feather')) } diff --git a/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/MappingIOTest.java b/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/MappingIOTest.java new file mode 100644 index 0000000..c33c09b --- /dev/null +++ b/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/MappingIOTest.java @@ -0,0 +1,42 @@ +package org.parchmentmc.feather.io.mappingio; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingUtil; +import net.fabricmc.mappingio.adapter.FlatAsRegularMappingVisitor; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; +import net.fabricmc.mappingio.tree.VisitableMappingTree; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class MappingIOTest { + @Test + public void testMio() throws IOException { + VisitableMappingTree tree = new MemoryMappingTree(); + String srcNs = MappingUtil.NS_SOURCE_FALLBACK; + + tree.visitNamespaces(srcNs, Collections.emptyList()); + + tree.visitClass("net/minecraft/class_1"); + tree.visitComment(MappedElementKind.CLASS, "class_1 comment"); + + tree.visitField("field_1", "I"); + tree.visitComment(MappedElementKind.FIELD, "field_1 comment"); + + tree.visitMethod("method_1", "()V"); + tree.visitComment(MappedElementKind.METHOD, "method_1 comment"); + + tree.visitMethodArg(-1, 0, "arg_1"); + tree.visitComment(MappedElementKind.METHOD_ARG, "arg_1 comment"); + + tree.visitEnd(); + + MappingTree convertedTree = MdcToMio.toTree(MioToMdc.fromTree(tree), srcNs); + + convertedTree.accept(new FlatAsRegularMappingVisitor(new SubsetAssertingVisitor(tree, null, null))); + tree.accept(new FlatAsRegularMappingVisitor(new SubsetAssertingVisitor(convertedTree, null, null))); + } +} diff --git a/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/SubsetAssertingVisitor.java b/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/SubsetAssertingVisitor.java new file mode 100644 index 0000000..7fc4633 --- /dev/null +++ b/io-mappingio/src/test/java/org/parchmentmc/feather/io/mappingio/SubsetAssertingVisitor.java @@ -0,0 +1,246 @@ +// Copied from https://github.com/FabricMC/mapping-io/blob/7e7e77ef06d155ccdf067463752e1ede702cb241/src/test/java/net/fabricmc/mappingio/SubsetAssertingVisitor.java + +package org.parchmentmc.feather.io.mappingio; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.mappingio.FlatMappingVisitor; +import net.fabricmc.mappingio.MappingUtil; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.tree.MappingTreeView; +import net.fabricmc.mappingio.tree.MappingTreeView.ClassMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.FieldMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.MethodArgMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.MethodMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.MethodVarMappingView; + +public class SubsetAssertingVisitor implements FlatMappingVisitor { + public SubsetAssertingVisitor(MappingTreeView supTree, @Nullable MappingFormat supFormat, @Nullable MappingFormat subFormat) { + this.supTree = supTree; + this.supDstNsCount = supTree.getMaxNamespaceId(); + this.subHasNamespaces = subFormat == null ? true : subFormat.hasNamespaces; + this.supHasNamespaces = supFormat == null ? true : supFormat.hasNamespaces; + this.supHasFieldDesc = supFormat == null ? true : supFormat.hasFieldDescriptors; + this.supHasArgs = supFormat == null ? true : supFormat.supportsArgs; + this.supHasVars = supFormat == null ? true : supFormat.supportsLocals; + this.supHasComments = supFormat == null ? true : supFormat.supportsComments; + } + + @Override + public void visitNamespaces(String srcNamespace, List dstNamespaces) throws IOException { + assertTrue(srcNamespace.equals(subHasNamespaces ? supTree.getSrcNamespace() : MappingUtil.NS_SOURCE_FALLBACK)); + this.dstNamespaces = dstNamespaces; + + if (!subHasNamespaces) { + assertTrue(dstNamespaces.size() == 1); + assertTrue(dstNamespaces.get(0).equals(MappingUtil.NS_TARGET_FALLBACK)); + return; + } + + for (int i = 0; i < dstNamespaces.size(); i++) { + String dstNs = dstNamespaces.get(i); + boolean contained = supTree.getDstNamespaces().contains(dstNs); + + if (!supHasNamespaces) { + if (contained) return; + } else { + assertTrue(contained); + } + } + + if (!supHasNamespaces) throw new RuntimeException("SubTree namespace not contained in SupTree"); + } + + @Override + public boolean visitClass(String srcName, String[] dstNames) throws IOException { + ClassMappingView supCls = supTree.getClass(srcName); + Map supDstNamesByNsName = new HashMap<>(); + + if (supCls == null) { + String[] tmpDst = supHasNamespaces ? dstNames : new String[]{dstNames[0]}; + if (!Arrays.stream(tmpDst).anyMatch(Objects::nonNull)) return false; + throw new RuntimeException("SubTree class not contained in SupTree: " + srcName); + } + + for (int supNs = 0; supNs < supDstNsCount; supNs++) { + supDstNamesByNsName.put(supTree.getNamespaceName(supNs), supCls.getDstName(supNs)); + } + + for (int subNs = 0; subNs < dstNames.length; subNs++) { + String supDstName = supDstNamesByNsName.get(dstNamespaces.get(subNs)); + if (!supHasNamespaces && supDstName == null) continue; + assertTrue(dstNames[subNs] == null || dstNames[subNs].equals(supDstName) || (supDstName == null && dstNames[subNs].equals(srcName))); + } + + return true; + } + + @Override + public void visitClassComment(String srcName, String[] dstNames, String comment) throws IOException { + if (!supHasComments) return; + assertEquals(supTree.getClass(srcName).getComment(), comment); + } + + @Override + public boolean visitField(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException { + FieldMappingView supFld = supTree.getClass(srcClsName).getField(srcName, srcDesc); + Map supDstDataByNsName = new HashMap<>(); + + if (supFld == null) { + String[] tmpDst = supHasNamespaces ? dstNames : new String[]{dstNames[0]}; + if (!Arrays.stream(tmpDst).anyMatch(Objects::nonNull)) return false; + throw new RuntimeException("SubTree field not contained in SupTree: " + srcName); + } + + for (int supNs = 0; supNs < supDstNsCount; supNs++) { + supDstDataByNsName.put(supTree.getNamespaceName(supNs), new String[]{supFld.getDstName(supNs), supFld.getDstDesc(supNs)}); + } + + for (int subNs = 0; subNs < dstNames.length; subNs++) { + String[] supDstData = supDstDataByNsName.get(dstNamespaces.get(subNs)); + if (!supHasNamespaces && supDstData == null) continue; + + String supDstName = supDstData[0]; + assertTrue(dstNames[subNs] == null || dstNames[subNs].equals(supDstName) || (supDstName == null && dstNames[subNs].equals(srcName))); + + if (!supHasFieldDesc) continue; + String supDstDesc = supDstData[1]; + assertTrue(dstDescs == null || dstDescs[subNs] == null || dstDescs[subNs].equals(supDstDesc)); + } + + return true; + } + + @Override + public void visitFieldComment(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, String comment) throws IOException { + if (!supHasComments) return; + assertEquals(supTree.getClass(srcClsName).getField(srcName, srcDesc).getComment(), comment); + } + + @Override + public boolean visitMethod(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException { + MethodMappingView supMth = supTree.getClass(srcClsName).getMethod(srcName, srcDesc); + Map supDstDataByNsName = new HashMap<>(); + + if (supMth == null) { + String[] tmpDst = supHasNamespaces ? dstNames : new String[]{dstNames[0]}; + if (!Arrays.stream(tmpDst).anyMatch(Objects::nonNull)) return false; + throw new RuntimeException("SubTree method not contained in SupTree: " + srcName); + } + + for (int supNs = 0; supNs < supDstNsCount; supNs++) { + supDstDataByNsName.put(supTree.getNamespaceName(supNs), new String[]{supMth.getDstName(supNs), supMth.getDstDesc(supNs)}); + } + + for (int subNs = 0; subNs < dstNames.length; subNs++) { + String[] supDstData = supDstDataByNsName.get(dstNamespaces.get(subNs)); + if (!supHasNamespaces && supDstData == null) continue; + + String supDstName = supDstData[0]; + assertTrue(dstNames[subNs] == null || dstNames[subNs].equals(supDstName) || (supDstName == null && dstNames[subNs].equals(srcName))); + + String supDstDesc = supDstData[1]; + assertTrue(dstDescs == null || dstDescs[subNs] == null || dstDescs[subNs].equals(supDstDesc)); + } + + return true; + } + + @Override + public void visitMethodComment(String srcClsName, String srcName, String srcDesc, + String[] dstClsNames, String[] dstNames, String[] dstDescs, String comment) throws IOException { + if (!supHasComments) return; + assertEquals(supTree.getClass(srcClsName).getMethod(srcName, srcDesc).getComment(), comment); + } + + @Override + public boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstNames) throws IOException { + if (!supHasArgs) return false; + MethodArgMappingView supArg = supTree.getClass(srcClsName).getMethod(srcMethodName, srcMethodDesc).getArg(argPosition, lvIndex, srcName); + Map supDstNamesByNsName = new HashMap<>(); + + if (supArg == null) { + String[] tmpDst = supHasNamespaces ? dstNames : new String[]{dstNames[0]}; + if (!Arrays.stream(tmpDst).anyMatch(Objects::nonNull)) return false; + throw new RuntimeException("SubTree arg not contained in SupTree: " + srcName); + } + + for (int supNs = 0; supNs < supDstNsCount; supNs++) { + supDstNamesByNsName.put(supTree.getNamespaceName(supNs), supArg.getDstName(supNs)); + } + + for (int subNs = 0; subNs < dstNames.length; subNs++) { + String supDstName = supDstNamesByNsName.get(dstNamespaces.get(subNs)); + if (!supHasNamespaces && supDstName == null) continue; + assertTrue(dstNames[subNs] == null || dstNames[subNs].equals(supDstName) || (supDstName == null && dstNames[subNs].equals(srcName))); + } + + return true; + } + + @Override + public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstNames, String comment) throws IOException { + if (!supHasComments) return; + assertEquals(supTree.getClass(srcClsName).getMethod(srcMethodName, srcMethodDesc).getArg(argPosition, lvIndex, srcArgName).getComment(), comment); + } + + @Override + public boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstNames) throws IOException { + if (!supHasVars) return false; + MethodVarMappingView supVar = supTree.getClass(srcClsName).getMethod(srcMethodName, srcMethodDesc).getVar(lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcName); + Map supDstNamesByNsName = new HashMap<>(); + + if (supVar == null) { + String[] tmpDst = supHasNamespaces ? dstNames : new String[]{dstNames[0]}; + if (!Arrays.stream(tmpDst).anyMatch(Objects::nonNull)) return false; + throw new RuntimeException("SubTree var not contained in SupTree: " + srcName); + } + + for (int supNs = 0; supNs < supDstNsCount; supNs++) { + supDstNamesByNsName.put(supTree.getNamespaceName(supNs), supVar.getDstName(supNs)); + } + + for (int subNs = 0; subNs < dstNames.length; subNs++) { + String supDstName = supDstNamesByNsName.get(dstNamespaces.get(subNs)); + if (!supHasNamespaces && supDstName == null) continue; + assertTrue(dstNames[subNs] == null || dstNames[subNs].equals(supDstName) || (supDstName == null && dstNames[subNs].equals(srcName))); + } + + return true; + } + + @Override + public void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc, + int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName, + String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstNames, String comment) throws IOException { + if (!supHasComments) return; + assertEquals(supTree.getClass(srcClsName).getMethod(srcMethodName, srcMethodDesc).getVar(lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName).getComment(), comment); + } + + private final MappingTreeView supTree; + private final int supDstNsCount; + private final boolean subHasNamespaces; + private final boolean supHasNamespaces; + private final boolean supHasFieldDesc; + private final boolean supHasArgs; + private final boolean supHasVars; + private final boolean supHasComments; + private List dstNamespaces; +}