mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 17:04:25 +00:00
test: strengthen ASM characterization gates
This commit is contained in:
1 parent
2e511a21e3
commit
a65207cc62
7 files changed
+383
-104
No files matched your search
@@ -14,18 +14,11 @@ import java.util.TreeSet;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
* Reflection and bytecode only: the companion's initializer reads the Forge config, so loading it in a plain JVM is not
|
||||
* possible. Dump content and the byte-count log are covered by the Forge suite.
|
||||
* Reflection only: initialization reads the Forge config. DebugPrinterFunctionalTest covers directory cleanup,
|
||||
* enabled/disabled dumping and byte-count logging under Forge.
|
||||
*/
|
||||
class DebugPrinterCharacterizationTest {
|
||||
|
||||
@@ -65,73 +58,6 @@ class DebugPrinterCharacterizationTest {
|
||||
assertEquals(5, DebugPrinter$.class.getDeclaredFields().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gatesOnTheConfigTagAndClearsTheDumpDirectoryOnce() throws Exception {
|
||||
ClassNode node = companion();
|
||||
assertEquals(signatures("debug_asm", "Multipart ASM", "asm/multipart"), constants(node, "<init>"));
|
||||
assertCall(node, "<init>", "codechicken/multipart/handler/MultipartProxy", "config");
|
||||
assertCall(node, "<init>", "java/io/File", "mkdirs");
|
||||
// The per-file delete itself is covered by the Forge suite, which compares generated dump names.
|
||||
assertCall(node, "<init>", "java/io/File", "listFiles");
|
||||
assertCall(node, "<init>", "java/io/File", "exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dumpsThroughASMHelperAndLogsEverySixteenThousandBytes() throws Exception {
|
||||
ClassNode node = companion();
|
||||
assertEquals(signatures(".txt"), constants(node, "dump"));
|
||||
assertCall(node, "dump", "codechicken/lib/asm/ASMHelper", "dump");
|
||||
|
||||
assertEquals(signatures(" bytes of permGen has been used by ASMMixinCompiler"), constants(node, "defined"));
|
||||
assertTrue(intOperands(node, "defined").contains(16000));
|
||||
assertCall(node, "defined", "org/apache/logging/log4j/Logger", "debug");
|
||||
}
|
||||
|
||||
private static ClassNode companion() throws Exception {
|
||||
ClassNode node = new ClassNode();
|
||||
new ClassReader(DebugPrinter$.class.getName()).accept(node, 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
private static Set<String> constants(ClassNode node, String methodName) {
|
||||
Set<String> found = new TreeSet<>();
|
||||
for (AbstractInsnNode instruction : instructions(node, methodName)) {
|
||||
if (instruction instanceof LdcInsnNode && ((LdcInsnNode) instruction).cst instanceof String) {
|
||||
found.add((String) ((LdcInsnNode) instruction).cst);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private static Set<Integer> intOperands(ClassNode node, String methodName) {
|
||||
Set<Integer> found = new TreeSet<>();
|
||||
for (AbstractInsnNode instruction : instructions(node, methodName)) {
|
||||
if (instruction instanceof IntInsnNode) {
|
||||
found.add(((IntInsnNode) instruction).operand);
|
||||
} else if (instruction instanceof LdcInsnNode && ((LdcInsnNode) instruction).cst instanceof Integer) {
|
||||
found.add((Integer) ((LdcInsnNode) instruction).cst);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private static void assertCall(ClassNode node, String methodName, String owner, String name) {
|
||||
for (AbstractInsnNode instruction : instructions(node, methodName)) {
|
||||
if (instruction instanceof MethodInsnNode) {
|
||||
MethodInsnNode call = (MethodInsnNode) instruction;
|
||||
if (call.owner.equals(owner) && call.name.equals(name)) return;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Missing call " + owner + '.' + name + " in " + methodName);
|
||||
}
|
||||
|
||||
private static AbstractInsnNode[] instructions(ClassNode node, String methodName) {
|
||||
for (MethodNode method : node.methods) {
|
||||
if (method.name.equals(methodName)) return method.instructions.toArray();
|
||||
}
|
||||
throw new AssertionError("Missing method " + methodName);
|
||||
}
|
||||
|
||||
private static void assertField(String name, Class<?> type, int modifiers) throws Exception {
|
||||
Field field = DebugPrinter$.class.getDeclaredField(name);
|
||||
assertSame(type, field.getType());
|
||||
|
||||
+2
-15
@@ -21,9 +21,8 @@ import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
* The companion's initializer reaches Forge through TileMultipart, so generation is exercised by the Forge suite:
|
||||
* generated tiles, pass-through delegation and the copied fields of Java traits. Surface and emission order are checked
|
||||
* here.
|
||||
* Surface and bytecode smoke checks only. Generation reaches Forge through ObfMapping; the Forge suite checks copyFrom
|
||||
* guards and emitted instructions, generated tiles and pass-through delegation.
|
||||
*/
|
||||
class MultipartMixinFactoryCharacterizationTest {
|
||||
|
||||
@@ -83,18 +82,6 @@ class MultipartMixinFactoryCharacterizationTest {
|
||||
assertCall(node, "onCompiled", "codechicken/multipart/MultipartGenerator$", "registerTileClass");
|
||||
}
|
||||
|
||||
@Test
|
||||
void completesJavaTraitsWithAGuardedCopyFrom() throws Exception {
|
||||
ClassNode node = companion();
|
||||
Set<String> constants = constants(node, "autoCompleteJavaTrait");
|
||||
assertTrue(constants.contains("copyFrom"));
|
||||
assertTrue(constants.contains("(Lcodechicken/multipart/TileMultipart;)V"));
|
||||
assertTrue(constants.contains("codechicken/multipart/TileMultipart"));
|
||||
// The existing-method guard runs before anything is emitted.
|
||||
assertCall(node, "autoCompleteJavaTrait", "codechicken/lib/asm/ASMHelper", "findMethod");
|
||||
assertCall(node, "autoCompleteJavaTrait", "org/objectweb/asm/MethodVisitor", "visitMaxs");
|
||||
}
|
||||
|
||||
@Test
|
||||
void namesAndFillsThePassThroughTrait() throws Exception {
|
||||
ClassNode node = companion();
|
||||
|
||||
@@ -74,11 +74,11 @@ class ScalaSigReaderCharacterizationTest {
|
||||
assertEquals("", ScalaSigReader.encode(new byte[0]));
|
||||
assertArrayEquals(new byte[0], ScalaSigReader.decode(""));
|
||||
|
||||
// The dropped trailing group is empty for a short payload, but carries the high bits of a long one.
|
||||
// The dropped group is zero for 42, but carries high bits for [0, 0xfe].
|
||||
byte[] single = { 42 };
|
||||
assertArrayEquals(single, ScalaSigReader.decode(ScalaSigReader.encode(single)));
|
||||
byte[] highBit = { 0, (byte) 0xfe };
|
||||
assertFalse(Arrays.equals(highBit, ScalaSigReader.decode(ScalaSigReader.encode(highBit))));
|
||||
assertArrayEquals(new byte[] { 0, 62 }, ScalaSigReader.decode(ScalaSigReader.encode(highBit)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user