summaryrefslogtreecommitdiffstats
path: root/dexcomparator
diff options
context:
space:
mode:
authorBenoit Lamarche <benoitlamarche@google.com>2014-07-07 15:00:15 +0200
committerBenoit Lamarche <benoitlamarche@google.com>2014-07-07 15:00:15 +0200
commit51ba3e36bbaa6a315d07137b6fb00be139919d18 (patch)
tree6268696f96d6382eb97c8568658cc702452aeb2a /dexcomparator
parent31b9d81989d65f2ecdeef7862d69702859d5ad11 (diff)
downloadtoolchain_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')
-rw-r--r--dexcomparator/.classpath2
-rw-r--r--dexcomparator/src/com/android/jack/util/BytesStreamSucker.java68
-rw-r--r--dexcomparator/src/com/android/jack/util/CharactersStreamSucker.java65
-rw-r--r--dexcomparator/src/com/android/jack/util/ExecuteFile.java274
-rw-r--r--dexcomparator/testing/com/android/jack/dexcomparator/test/BinaryCodeComparisonTest.java101
-rw-r--r--dexcomparator/testsource1/com/android/jack/dexcomparator/test/A.java26
-rw-r--r--dexcomparator/testsource2/com/android/jack/dexcomparator/test/A.java26
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;
+ }
+
+}