Mercurial > hg > icedrobot > daneel
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(); + } + }; +}