changeset 74:4ccee27bea05

Added new test runner for Daneel JUnit tests. Reviewed-by: Volker Berlin * pom.xml: Added dependency to dx tool with test scope. * DexifyingRunner.java: New runner, use with @RunWith annotation. * rewriter/InvokeWithoutMoveResultTest.java: Sample test case.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Thu, 24 Mar 2011 18:30:09 +0100
parents 15cfcc7a8756
children fc5234735b4b
files pom.xml src/test/java/org/icedrobot/daneel/DexifyingRunner.java src/test/java/org/icedrobot/daneel/rewriter/InvokeWithoutMoveResultTest.java
diffstat 3 files changed, 254 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/pom.xml	Thu Mar 24 18:15:14 2011 +0100
+++ b/pom.xml	Thu Mar 24 18:30:09 2011 +0100
@@ -104,5 +104,11 @@
     	<version>1.8.5</version>
     	<scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.android.dx</groupId>
+      <artifactId>dx</artifactId>
+      <version>1.0</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/java/org/icedrobot/daneel/DexifyingRunner.java	Thu Mar 24 18:30:09 2011 +0100
@@ -0,0 +1,129 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is subject to the "Classpath" exception:
+ *
+ * Linking this library statically or dynamically with other modules is
+ * making a combined work based on this library.  Thus, the terms and
+ * conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under terms
+ * of your choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.  An
+ * independent module is a module which is not derived from or based on
+ * this library.  If you modify this library, you may extend this exception
+ * to your version of the library, but you are not obligated to do so.  If
+ * you do not wish to do so, delete this exception statement from your
+ * version.
+ */
+
+package org.icedrobot.daneel;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+
+import org.icedrobot.daneel.dex.DexFile;
+import org.icedrobot.daneel.rewriter.DexRewriter;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+import com.android.dx.command.dexer.Main;
+import com.android.dx.command.dexer.Main.Arguments;
+
+/**
+ * A JUnit runner implementation which makes sure that test code is pushed
+ * through the Daneel stack before running a test case. The test code should be
+ * a static inner class of the test case and be named {@code DEXCode} for this
+ * runner to recognize it.
+ */
+public class DexifyingRunner extends BlockJUnit4ClassRunner {
+
+    /**
+     * Constructs a new JUnit test runner for the given test class. Keep a
+     * constructor with that signature around, so that the JUnit framework can
+     * instantiate us for {@link org.junit.runner.RunWith} annotations.
+     * 
+     * @param testClass The given test class.
+     */
+    public DexifyingRunner(Class<?> testClass) throws InitializationError {
+        super(testClass);
+    }
+
+    /**
+     * Overridden to add pre-processing of test code within our test classes.
+     * This is what triggers the test code to be pushed through Daneel.
+     */
+    @Override
+    protected Statement classBlock(RunNotifier notifier) {
+        try {
+            rewriteTestCode();
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return super.classBlock(notifier);
+    }
+
+    /**
+     * Performs the steps necessary to push the test code through Daneel.
+     */
+    private void rewriteTestCode() throws Exception {
+        File tmp = File.createTempFile("daneel-dexifier", ".dex");
+        tmp.deleteOnExit();
+
+        // Find the original test class file.
+        String testClass = getTestClass().getJavaClass().getName() + "$DEXCode";
+        String srcName = testClass.replace('.', '/') + ".class";
+        URL srcURL = ClassLoader.getSystemResource(srcName);
+        if (srcURL == null)
+            throw new DaneelException("Unable to find test code.");
+        String src = srcURL.getFile();
+        if (src == null)
+            throw new DaneelException("Unable to convert URL to path.");
+
+        // Convert test code into DEX by running it through the 'dx' tool.
+        String[] args = new String[] { "--no-strict", "--output=" + tmp, src };
+        Arguments arguments = new Arguments();
+        arguments.parse(args);
+        int dexerResult = Main.run(arguments);
+        if (dexerResult != 0)
+            throw new DaneelException("Unable to execute 'dx' tool.");
+
+        // Rewrite test code from DEX back to Java using our rewriter.
+        DexFile dex = DexFile.parse(tmp);
+        byte[] testCode = DexRewriter.rewrite(testClass, dex);
+
+        // From this point on we no longer need the DEX file
+        if (!tmp.delete())
+            throw new DaneelException("Unable to delete DEX file.");
+
+        // Load the rewritten test code using the system class loader.
+        ClassLoader cl = ClassLoader.getSystemClassLoader();
+        Method m = ClassLoader.class.getDeclaredMethod("defineClass",
+                String.class, byte[].class, int.class, int.class);
+        m.setAccessible(true);
+        m.invoke(cl, testClass, testCode, 0, testCode.length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/java/org/icedrobot/daneel/rewriter/InvokeWithoutMoveResultTest.java	Thu Mar 24 18:30:09 2011 +0100
@@ -0,0 +1,119 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is subject to the "Classpath" exception:
+ *
+ * Linking this library statically or dynamically with other modules is
+ * making a combined work based on this library.  Thus, the terms and
+ * conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under terms
+ * of your choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.  An
+ * independent module is a module which is not derived from or based on
+ * this library.  If you modify this library, you may extend this exception
+ * to your version of the library, but you are not obligated to do so.  If
+ * you do not wish to do so, delete this exception statement from your
+ * version.
+ */
+
+package org.icedrobot.daneel.rewriter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.icedrobot.daneel.DexifyingRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DexifyingRunner.class)
+public class InvokeWithoutMoveResultTest {
+
+    @Test
+    public void test() {
+        DEXCode.invoke();
+    }
+
+    @Test
+    public void testBoolean() {
+        assertFalse(DEXCode.f_boolean());
+    }
+
+    @Test
+    public void testByte() {
+        assertEquals(0, DEXCode.f_byte());
+    }
+
+    @Test
+    public void testChar() {
+        assertEquals(0, DEXCode.f_char());
+    }
+
+    @Test
+    public void testShort() {
+        assertEquals(0, DEXCode.f_short());
+    }
+
+    @Test
+    public void testInt() {
+        assertEquals(0, DEXCode.f_int());
+    }
+
+    @Test
+    public void testLong() {
+        assertEquals(0, DEXCode.f_long());
+    }
+
+    @Test
+    public void testFloat() {
+        assertEquals(0, DEXCode.f_float(), 0);
+    }
+
+    @Test
+    public void testDouble() {
+        assertEquals(0, DEXCode.f_double(), 0);
+    }
+
+    // Keep this class named "DEXCode" to push it through Daneel.
+    private static class DEXCode {
+        static void f_void() { }
+        static boolean f_boolean() { return false; }
+        static byte f_byte() { return 0; }
+        static char f_char() { return 0; }
+        static short f_short() { return 0; }
+        static int f_int() { return 0; }
+        static long f_long() { return 0; }
+        static float f_float() { return 0; }
+        static double f_double() { return 0; }
+
+        public static void invoke() {
+            f_void();
+            f_boolean();
+            f_byte();
+            f_char();
+            f_short();
+            f_int();
+            f_long();
+            f_float();
+            f_double();
+        }
+    };
+}