diff options
author | Benoit Lamarche <benoitlamarche@google.com> | 2014-07-07 15:00:15 +0200 |
---|---|---|
committer | Benoit Lamarche <benoitlamarche@google.com> | 2014-07-07 15:00:15 +0200 |
commit | 51ba3e36bbaa6a315d07137b6fb00be139919d18 (patch) | |
tree | 6268696f96d6382eb97c8568658cc702452aeb2a /dexcomparator | |
parent | 31b9d81989d65f2ecdeef7862d69702859d5ad11 (diff) | |
download | toolchain_jack-51ba3e36bbaa6a315d07137b6fb00be139919d18.zip toolchain_jack-51ba3e36bbaa6a315d07137b6fb00be139919d18.tar.gz toolchain_jack-51ba3e36bbaa6a315d07137b6fb00be139919d18.tar.bz2 |
Add DexComparator code binary comparison test
Change-Id: Id5c0c47e8a2a92dbd689d9e418c00a8f9ecbe30f
Diffstat (limited to 'dexcomparator')
7 files changed, 562 insertions, 0 deletions
diff --git a/dexcomparator/.classpath b/dexcomparator/.classpath index e545d84..127f03f 100644 --- a/dexcomparator/.classpath +++ b/dexcomparator/.classpath @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="testing"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/Dx"/> <classpathentry kind="src" path=".apt_generated"> @@ -10,5 +11,6 @@ </classpathentry> <classpathentry kind="lib" path="libs/dex-lib.jar"/> <classpathentry kind="lib" path="libs/jsr305-lib.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/dexcomparator/src/com/android/jack/util/BytesStreamSucker.java b/dexcomparator/src/com/android/jack/util/BytesStreamSucker.java new file mode 100644 index 0000000..e68a367 --- /dev/null +++ b/dexcomparator/src/com/android/jack/util/BytesStreamSucker.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.annotation.Nonnull; + +/** + * Class that continuously read an {@link InputStream} and optionally could write the input in a + * {@link OutputStream}. + */ +public class BytesStreamSucker { + + private static final int BUFFER_SIZE = 4096; + + @Nonnull + private final byte[] buffer = new byte[BUFFER_SIZE]; + + @Nonnull + private final InputStream is; + + @Nonnull + private final OutputStream os; + + private final boolean toBeClose; + + public BytesStreamSucker( + @Nonnull InputStream is, @Nonnull OutputStream os, boolean toBeClose) { + this.is = is; + this.os = os; + this.toBeClose = toBeClose; + } + + public BytesStreamSucker(@Nonnull InputStream is, @Nonnull OutputStream os) { + this(is, os, false); + } + + public void suck() throws IOException { + try { + int bytesRead; + while ((bytesRead = is.read(buffer)) >= 0) { + os.write(buffer, 0, bytesRead); + os.flush(); + } + } finally { + if (toBeClose) { + os.close(); + } + } + } +}
\ No newline at end of file diff --git a/dexcomparator/src/com/android/jack/util/CharactersStreamSucker.java b/dexcomparator/src/com/android/jack/util/CharactersStreamSucker.java new file mode 100644 index 0000000..16e63cf --- /dev/null +++ b/dexcomparator/src/com/android/jack/util/CharactersStreamSucker.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; + +import javax.annotation.Nonnull; + +/** + * Class that continuously read an {@link InputStream} and optionally could print the input in a + * {@link PrintStream}. + */ +public class CharactersStreamSucker { + + @Nonnull + private final BufferedReader ir; + + @Nonnull + private final PrintStream os; + + private final boolean toBeClose; + + public CharactersStreamSucker( + @Nonnull InputStream is, @Nonnull PrintStream os, boolean toBeClose) { + this.ir = new BufferedReader(new InputStreamReader(is)); + this.os = os; + this.toBeClose = toBeClose; + } + + public CharactersStreamSucker(@Nonnull InputStream is, @Nonnull PrintStream os) { + this(is, os, false); + } + + public void suck() throws IOException { + String line; + + try { + while ((line = ir.readLine()) != null) { + os.println(line); + } + } finally { + if (toBeClose) { + os.close(); + } + } + } +}
\ No newline at end of file diff --git a/dexcomparator/src/com/android/jack/util/ExecuteFile.java b/dexcomparator/src/com/android/jack/util/ExecuteFile.java new file mode 100644 index 0000000..869158d --- /dev/null +++ b/dexcomparator/src/com/android/jack/util/ExecuteFile.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Class to handle the execution of an external process + */ +public class ExecuteFile { + @Nonnull + private final String[] cmdLine; + + @CheckForNull + private File workDir; + + @CheckForNull + private InputStream inStream; + private boolean inToBeClose; + + @CheckForNull + private OutputStream outStream; + private boolean outToBeClose; + + @CheckForNull + private OutputStream errStream; + private boolean errToBeClose; + private boolean verbose; + + @Nonnull + private final Logger logger = Logger.getLogger(this.getClass().getName()); + + public void setErr(@Nonnull File file) throws FileNotFoundException { + errStream = new FileOutputStream(file); + errToBeClose = true; + } + + public void setOut(@Nonnull File file) throws FileNotFoundException { + outStream = new FileOutputStream(file); + outToBeClose = true; + } + + public void setIn(@Nonnull File file) throws FileNotFoundException { + inStream = new FileInputStream(file); + inToBeClose = true; + } + + public void setErr(@Nonnull OutputStream stream) { + errStream = stream; + } + + public void setOut(@Nonnull OutputStream stream) { + outStream = stream; + } + + public void setIn(@Nonnull InputStream stream) { + inStream = stream; + } + + public void setWorkingDir(@Nonnull File dir, boolean create) throws IOException { + if (!dir.isDirectory()) { + if (create && !dir.exists()) { + if (!dir.mkdirs()) { + throw new IOException("Directory creation failed"); + } + } else { + throw new FileNotFoundException(dir.getPath() + " is not a directory"); + } + } + + workDir = dir; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public ExecuteFile(@Nonnull File exec, @Nonnull String[] args) { + cmdLine = new String[args.length + 1]; + System.arraycopy(args, 0, cmdLine, 1, args.length); + + cmdLine[0] = exec.getAbsolutePath(); + } + + public ExecuteFile(@Nonnull String exec, @Nonnull String[] args) { + cmdLine = new String[args.length + 1]; + System.arraycopy(args, 0, cmdLine, 1, args.length); + + cmdLine[0] = exec; + } + + public ExecuteFile(@Nonnull File exec) { + cmdLine = new String[1]; + cmdLine[0] = exec.getAbsolutePath(); + } + + public ExecuteFile(@Nonnull String[] cmdLine) { + this.cmdLine = cmdLine.clone(); + } + + public ExecuteFile(@Nonnull String cmdLine) throws IOException { + StringReader reader = new StringReader(cmdLine); + StreamTokenizer tokenizer = new StreamTokenizer(reader); + tokenizer.resetSyntax(); + // Only standard spaces are recognized as whitespace chars + tokenizer.whitespaceChars(' ', ' '); + // Matches alphanumerical and common special symbols like '(' and ')' + tokenizer.wordChars('!', 'z'); + // Quote chars will be ignored when parsing strings + tokenizer.quoteChar('\''); + tokenizer.quoteChar('\"'); + ArrayList<String> tokens = new ArrayList<String>(); + while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) { + String token = tokenizer.sval; + if (token != null) { + tokens.add(token); + } + } + this.cmdLine = tokens.toArray(new String[0]); + } + + public boolean run() { + int ret; + Process proc = null; + Thread suckOut = null; + Thread suckErr = null; + Thread suckIn = null; + + try { + StringBuilder cmdLineBuilder = new StringBuilder(); + for (String arg : cmdLine) { + cmdLineBuilder.append(arg).append(' '); + } + if (verbose) { + PrintStream printStream; + if (outStream instanceof PrintStream) { + printStream = (PrintStream) outStream; + } else { + printStream = System.out; + } + + if (printStream != null) { + printStream.println(cmdLineBuilder); + } + } else { + logger.log(Level.INFO, "Execute: {0}", cmdLineBuilder); + } + + proc = Runtime.getRuntime().exec(cmdLine, null, workDir); + + InputStream localInStream = inStream; + if (localInStream != null) { + suckIn = new Thread( + new ThreadBytesStreamSucker(localInStream, proc.getOutputStream(), inToBeClose)); + } else { + proc.getOutputStream().close(); + } + + OutputStream localOutStream = outStream; + if (localOutStream != null) { + if (localOutStream instanceof PrintStream) { + suckOut = new Thread(new ThreadCharactersStreamSucker(proc.getInputStream(), + (PrintStream) localOutStream, outToBeClose)); + } else { + suckOut = new Thread( + new ThreadBytesStreamSucker(proc.getInputStream(), localOutStream, outToBeClose)); + } + } + + OutputStream localErrStream = errStream; + if (localErrStream != null) { + if (localErrStream instanceof PrintStream) { + suckErr = new Thread(new ThreadCharactersStreamSucker(proc.getErrorStream(), + (PrintStream) localErrStream, errToBeClose)); + } else { + suckErr = new Thread( + new ThreadBytesStreamSucker(proc.getErrorStream(), localErrStream, errToBeClose)); + } + } + + if (suckIn != null) { + suckIn.start(); + } + if (suckOut != null) { + suckOut.start(); + } + if (suckErr != null) { + suckErr.start(); + } + + proc.waitFor(); + if (suckIn != null) { + suckIn.join(); + } + if (suckOut != null) { + suckOut.join(); + } + if (suckErr != null) { + suckErr.join(); + } + + ret = proc.exitValue(); + proc.destroy(); + + return ret == 0; + } catch (Throwable e) { + return false; + } + } + + private static class ThreadBytesStreamSucker extends BytesStreamSucker implements Runnable { + + public ThreadBytesStreamSucker(@Nonnull InputStream is, @Nonnull OutputStream os, + boolean toBeClose) { + super(is, os, toBeClose); + } + + @Override + public void run() { + try { + suck(); + } catch (IOException e) { + // Best effort + } + } + } + + private static class ThreadCharactersStreamSucker extends CharactersStreamSucker implements + Runnable { + + public ThreadCharactersStreamSucker(@Nonnull InputStream is, @Nonnull PrintStream ps, + boolean toBeClose) { + super(is, ps, toBeClose); + } + + @Override + public void run() { + try { + suck(); + } catch (IOException e) { + // Best effort + } + } + } +}
\ No newline at end of file diff --git a/dexcomparator/testing/com/android/jack/dexcomparator/test/BinaryCodeComparisonTest.java b/dexcomparator/testing/com/android/jack/dexcomparator/test/BinaryCodeComparisonTest.java new file mode 100644 index 0000000..863d291 --- /dev/null +++ b/dexcomparator/testing/com/android/jack/dexcomparator/test/BinaryCodeComparisonTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.dexcomparator.test; + +import com.android.jack.DexComparator; +import com.android.jack.DifferenceFoundException; +import com.android.jack.util.ExecuteFile; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import javax.annotation.Nonnull; + + +public class BinaryCodeComparisonTest { + + @Nonnull + private static final File testSource1 = new File("testsource1"); + @Nonnull + private static final File testSource2 = new File("testsource2"); + @Nonnull + private static final File jackJar = new File("../jack/dist/jack.jar"); + @Nonnull + private static final File coreStubsMini = new File("../jack/libs/core-stubs-mini.jar"); + + @BeforeClass + public static void setUpClass() { + BinaryCodeComparisonTest.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void testDifferentBinaryCodeComparison() throws IOException { + String sourcePath = "com/android/jack/dexcomparator/test/A.java"; + File a1 = new File(testSource1, sourcePath); + File a2 = new File(testSource2, sourcePath); + File dex1 = File.createTempFile("dex1", ".dex"); + dex1.deleteOnExit(); + compileToDexWithJack(a1, dex1); + File dex2 = File.createTempFile("dex2", ".dex"); + dex2.deleteOnExit(); + compileToDexWithJack(a2, dex2); + try { + new DexComparator().compare(dex1, dex2, false /* compareDebugInfo */, true /* strict */, + false /* compareDebugInfoBinarily */, true /* compareCodeBinarily */); + Assert.fail(); + } catch (DifferenceFoundException e) { + } + try { + new DexComparator().compare(dex2, dex1, false /* compareDebugInfo */, true /* strict */, + false /* compareDebugInfoBinarily */, true /* compareCodeBinarily */); + Assert.fail(); + } catch (DifferenceFoundException e) { + } + } + + @Test + public void testIdenticalBinaryCodeComparison() throws IOException { + String sourcePath = "com/android/jack/dexcomparator/test/A.java"; + File a1 = new File(testSource1, sourcePath); + File dex1 = File.createTempFile("dex1", ".dex"); + dex1.deleteOnExit(); + compileToDexWithJack(a1, dex1); + try { + new DexComparator().compare(dex1, dex1, false /* compareDebugInfo */, true /* strict */, + false /* compareDebugInfoBinarily */, true /* compareCodeBinarily */); + } catch (DifferenceFoundException e) { + Assert.fail(e.getMessage()); + } + } + + private void compileToDexWithJack(File source, File dex) { + String[] args = new String[]{"java", "-jar", jackJar.getAbsolutePath(), + "-cp", coreStubsMini.getAbsolutePath(), + "-o", dex.getAbsolutePath(), "--ecj", source.getAbsolutePath()}; + + ExecuteFile execFile = new ExecuteFile(args); + if (!execFile.run()) { + throw new RuntimeException("Jack exited with an error"); + } + + } + +} diff --git a/dexcomparator/testsource1/com/android/jack/dexcomparator/test/A.java b/dexcomparator/testsource1/com/android/jack/dexcomparator/test/A.java new file mode 100644 index 0000000..3cad8ce --- /dev/null +++ b/dexcomparator/testsource1/com/android/jack/dexcomparator/test/A.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.dexcomparator.test; + +public class A { + + private int m(int b) { + int a = b + 2; + return a * 3; + } + +} diff --git a/dexcomparator/testsource2/com/android/jack/dexcomparator/test/A.java b/dexcomparator/testsource2/com/android/jack/dexcomparator/test/A.java new file mode 100644 index 0000000..8d7919a --- /dev/null +++ b/dexcomparator/testsource2/com/android/jack/dexcomparator/test/A.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.jack.dexcomparator.test; + +public class A { + + private int m(int b) { + int a = b + 2; + return a * 4; + } + +} |