From 49c5ceb06ee42347b3b684fcd785e943bef28c3c Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 9 Sep 2013 23:21:47 +0400 Subject: [PATCH] core: add framework for internal tests --- .../src/main/java/jadx/api/Decompiler.java | 4 + .../test/java/jadx/api/InternalJadxTest.java | 116 ++++++++++++++++++ .../java/jadx/api/TestFieldIncrement.java | 41 +++++++ 3 files changed, 161 insertions(+) create mode 100644 jadx-core/src/test/java/jadx/api/InternalJadxTest.java create mode 100644 jadx-core/src/test/java/jadx/api/TestFieldIncrement.java diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index 9ccc3d3d..f5f64055 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -191,4 +191,8 @@ public final class Decompiler { LOG.error("Process class error", e); } } + + RootNode getRoot() { + return root; + } } diff --git a/jadx-core/src/test/java/jadx/api/InternalJadxTest.java b/jadx-core/src/test/java/jadx/api/InternalJadxTest.java new file mode 100644 index 00000000..47b07091 --- /dev/null +++ b/jadx-core/src/test/java/jadx/api/InternalJadxTest.java @@ -0,0 +1,116 @@ +package jadx.api; + +import jadx.core.Jadx; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.visitors.DepthTraverser; +import jadx.core.dex.visitors.IDexTreeVisitor; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; + +public abstract class InternalJadxTest { + + public ClassNode getClassNode(Class clazz) { + try { + File temp = getJarForClass(clazz); + + Decompiler d = new Decompiler(); + try { + d.loadFile(temp); + assertEquals(d.getClasses().size(), 1); + } catch (Exception e) { + fail(e.getMessage()); + } finally { + temp.delete(); + } + + List classes = d.getRoot().getClasses(false); + ClassNode cls = classes.get(0); + + assertEquals(cls.getFullName(), clazz.getName()); + + cls.load(); + List passes = Jadx.getPassesList(new DefaultJadxArgs(), null); + for (IDexTreeVisitor visitor : passes) { + DepthTraverser.visit(visitor, cls); + } + return cls; + } catch (Exception e) { + fail(e.getMessage()); + return null; + } + } + + protected MethodNode getMethod(ClassNode cls, String method) { + for (MethodNode mth : cls.getMethods()) { + if (mth.getName().equals(method)) { + return mth; + } + } + fail("Method not found " + method + " in class " + cls); + return null; + } + + public File getJarForClass(Class cls) throws IOException { + File classFile = getClassFile(cls); + String shortClsFileName = cls.getName().replace('.', '/') + ".class"; + + File temp = File.createTempFile("jadx-tmp-", System.nanoTime() + ".jar"); + JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp)); + add(classFile, shortClsFileName, jo); + jo.close(); + temp.deleteOnExit(); + return temp; + } + + private File getClassFile(Class cls) { + String path = cutPackage(cls) + ".class"; + URL resource = cls.getResource(path); + if (resource == null) { + throw new AssertionError("Class file not found: " + path); + } + if (!"file".equalsIgnoreCase(resource.getProtocol())) { + throw new IllegalStateException("Class is not stored in a file."); + } + return new File(resource.getPath()); + } + + private String cutPackage(Class cls) { + String longName = cls.getName(); + String pkg = cls.getPackage().getName(); + return longName.substring(pkg.length() + 1, longName.length()); + } + + private void add(File source, String entryName, JarOutputStream target) throws IOException { + BufferedInputStream in = null; + try { + JarEntry entry = new JarEntry(entryName); + entry.setTime(source.lastModified()); + target.putNextEntry(entry); + in = new BufferedInputStream(new FileInputStream(source)); + + byte[] buffer = new byte[1024]; + while (true) { + int count = in.read(buffer); + if (count == -1) + break; + target.write(buffer, 0, count); + } + target.closeEntry(); + } finally { + if (in != null) + in.close(); + } + } +} diff --git a/jadx-core/src/test/java/jadx/api/TestFieldIncrement.java b/jadx-core/src/test/java/jadx/api/TestFieldIncrement.java new file mode 100644 index 00000000..38b5374d --- /dev/null +++ b/jadx-core/src/test/java/jadx/api/TestFieldIncrement.java @@ -0,0 +1,41 @@ +package jadx.api; + +import jadx.core.dex.instructions.ArithNode; +import jadx.core.dex.instructions.ArithOp; +import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; + +import java.util.List; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestFieldIncrement extends InternalJadxTest { + + public static class TestCls { + public int field = 1; + + public void method() { + field++; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + MethodNode mth = getMethod(cls, "method"); + + List insns = mth.getBasicBlocks().get(0).getInstructions(); + assertEquals(insns.size(), 1); + InsnNode insnNode = insns.get(0); + assertEquals(InsnType.ARITH, insnNode.getType()); + assertEquals(ArithOp.ADD, ((ArithNode) insnNode).getOp()); + + assertThat(cls.getCode().toString(), containsString("field++;")); + } +} -- GitLab