summaryrefslogtreecommitdiffstats
path: root/jack-tests/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'jack-tests/src/com/android')
-rw-r--r--jack-tests/src/com/android/jack/test/category/RuntimeRegressionTest.java25
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/Comparator.java28
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorComposite.java46
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorDex.java81
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorDexAnnotations.java44
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorDiff.java60
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorException.java44
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorFile.java37
-rw-r--r--jack-tests/src/com/android/jack/test/comparator/ComparatorMapping.java44
-rw-r--r--jack-tests/src/com/android/jack/test/helper/CheckDexStructureTestHelper.java37
-rw-r--r--jack-tests/src/com/android/jack/test/helper/ErrorTestHelper.java75
-rw-r--r--jack-tests/src/com/android/jack/test/helper/GenericComparisonTestHelper.java45
-rw-r--r--jack-tests/src/com/android/jack/test/helper/IncrementalTestHelper.java169
-rw-r--r--jack-tests/src/com/android/jack/test/helper/JackDexMergerTestHelper.java97
-rw-r--r--jack-tests/src/com/android/jack/test/helper/RuntimeTestHelper.java373
-rw-r--r--jack-tests/src/com/android/jack/test/helper/SourceToDexComparisonTestHelper.java166
-rw-r--r--jack-tests/src/com/android/jack/test/runner/AbstractRuntimeRunner.java73
-rw-r--r--jack-tests/src/com/android/jack/test/runner/ArtRunnerDevice.java70
-rw-r--r--jack-tests/src/com/android/jack/test/runner/ArtRunnerHost.java66
-rw-r--r--jack-tests/src/com/android/jack/test/runner/DalvikRunner.java49
-rw-r--r--jack-tests/src/com/android/jack/test/runner/DalvikRunnerDevice.java81
-rw-r--r--jack-tests/src/com/android/jack/test/runner/DalvikRunnerHost.java78
-rw-r--r--jack-tests/src/com/android/jack/test/runner/DeviceRunner.java336
-rw-r--r--jack-tests/src/com/android/jack/test/runner/HostRunner.java52
-rw-r--r--jack-tests/src/com/android/jack/test/runner/RuntimeRunner.java46
-rw-r--r--jack-tests/src/com/android/jack/test/runner/RuntimeRunnerException.java40
-rw-r--r--jack-tests/src/com/android/jack/test/runner/RuntimeRunnerFactory.java87
-rw-r--r--jack-tests/src/com/android/jack/test/runtime/RuntimeTest.java43
-rw-r--r--jack-tests/src/com/android/jack/test/runtime/RuntimeTestInfo.java39
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java566
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/AndroidToolchain.java49
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/DummyToolchain.java76
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/IToolchain.java81
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/JackApiToolchain.java230
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java88
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java281
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java69
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java229
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java293
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/TestConfigurationException.java43
-rw-r--r--jack-tests/src/com/android/jack/test/toolchain/Toolchain.java154
-rw-r--r--jack-tests/src/com/android/jack/test/util/BytesStreamSucker.java74
-rw-r--r--jack-tests/src/com/android/jack/test/util/CharactersStreamSucker.java65
-rw-r--r--jack-tests/src/com/android/jack/test/util/ExecFileException.java65
-rw-r--r--jack-tests/src/com/android/jack/test/util/ExecuteFile.java292
-rw-r--r--jack-tests/src/com/android/jack/test/util/NullPrintStream.java181
46 files changed, 5267 insertions, 0 deletions
diff --git a/jack-tests/src/com/android/jack/test/category/RuntimeRegressionTest.java b/jack-tests/src/com/android/jack/test/category/RuntimeRegressionTest.java
new file mode 100644
index 0000000..b969a69
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/category/RuntimeRegressionTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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.test.category;
+
+/**
+ * Tests that can be grouped in one compilation step for
+ * regression tests.
+ */
+public interface RuntimeRegressionTest {
+
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/Comparator.java b/jack-tests/src/com/android/jack/test/comparator/Comparator.java
new file mode 100644
index 0000000..794a465
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/Comparator.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DifferenceFoundException;
+
+/**
+ * Common interface for comparator.
+ */
+public interface Comparator {
+
+ void compare() throws DifferenceFoundException, ComparatorException;
+
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorComposite.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorComposite.java
new file mode 100644
index 0000000..438e78e
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorComposite.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DifferenceFoundException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {link Comparator} is composed of one or several comparators.
+ */
+public class ComparatorComposite implements Comparator {
+
+ @Nonnull
+ private List<Comparator> comparators = new ArrayList<Comparator>();
+
+ public ComparatorComposite(@Nonnull Comparator... comparators) {
+ for (Comparator comparator : comparators) {
+ this.comparators.add(comparator);
+ }
+ }
+
+ @Override
+ public void compare() throws DifferenceFoundException, ComparatorException {
+ for (Comparator comparator : comparators) {
+ comparator.compare();
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorDex.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorDex.java
new file mode 100644
index 0000000..ddfa3fd
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorDex.java
@@ -0,0 +1,81 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DexComparator;
+import com.android.jack.DifferenceFoundException;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link Comparator} is used ton compare dex files.
+ */
+public class ComparatorDex extends ComparatorFile {
+
+ private boolean withDebugInfo = false;
+ private boolean strict = false;
+ private boolean compareDebugInfoBinary = false;
+ private boolean compareInstructionNumber = false;
+ private float instructionNumberTolerance = 0f;
+
+ public ComparatorDex(@Nonnull File candidate, @Nonnull File reference) {
+ super(candidate, reference);
+ }
+
+ @Nonnull
+ public ComparatorDex setWithDebugInfo(boolean withDebugInfo) {
+ this.withDebugInfo = withDebugInfo;
+ return this;
+ }
+
+ @Nonnull
+ public ComparatorDex setStrict(boolean strict) {
+ this.strict = strict;
+ return this;
+ }
+
+ @Nonnull
+ public ComparatorDex setCompareDebugInfoBinary(boolean compareDebugInfoBinary) {
+ this.compareDebugInfoBinary = compareDebugInfoBinary;
+ return this;
+ }
+
+ @Nonnull
+ public ComparatorDex setCompareInstructionNumber(boolean compareInstructionNumber) {
+ this.compareInstructionNumber = compareInstructionNumber;
+ return this;
+ }
+
+ @Nonnull
+ public ComparatorDex setInstructionNumberTolerance(float instructionNumberTolerance) {
+ this.instructionNumberTolerance = instructionNumberTolerance;
+ return this;
+ }
+
+ @Override
+ public void compare() throws DifferenceFoundException, ComparatorException {
+ try {
+ new DexComparator(withDebugInfo, strict, compareDebugInfoBinary, compareInstructionNumber,
+ instructionNumberTolerance).compare(reference, candidate);
+ } catch (IOException e) {
+ throw new ComparatorException(e);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorDexAnnotations.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorDexAnnotations.java
new file mode 100644
index 0000000..a72c319
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorDexAnnotations.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DexAnnotationsComparator;
+import com.android.jack.DifferenceFoundException;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link Comparator} is used to compare dex files annotations.
+ */
+public class ComparatorDexAnnotations extends ComparatorFile {
+
+ public ComparatorDexAnnotations(@Nonnull File candidate, @Nonnull File reference) {
+ super(candidate, reference);
+ }
+
+ @Override
+ public void compare() throws DifferenceFoundException, ComparatorException {
+ try {
+ new DexAnnotationsComparator().compare(reference, candidate);
+ } catch (IOException e) {
+ throw new ComparatorException(e);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorDiff.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorDiff.java
new file mode 100644
index 0000000..9c44470
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorDiff.java
@@ -0,0 +1,60 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DifferenceFoundException;
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link Comparator} uses the {@code diff} shell command to compare 2 files.
+ */
+public class ComparatorDiff extends ComparatorFile {
+
+ public ComparatorDiff(@Nonnull File candidate, @Nonnull File reference) {
+ super(candidate, reference);
+ }
+
+ @Override
+ public void compare() throws DifferenceFoundException, ComparatorException {
+ try {
+ ExecuteFile ef = new ExecuteFile(
+ "diff " + candidate.getAbsolutePath() + " " + reference.getAbsolutePath());
+ ef.setOut(System.out);
+ ef.setErr(System.err);
+
+ int exitStatus = ef.run();
+ switch (exitStatus) {
+ case 0:
+ break;
+ case 1:
+ throw new DifferenceFoundException();
+ default:
+ throw new ComparatorException("Exit status: " + exitStatus);
+ }
+ } catch (ExecFileException e) {
+ throw new ComparatorException(e);
+ } catch (IOException e) {
+ throw new ComparatorException(e);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorException.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorException.java
new file mode 100644
index 0000000..037a501
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorException.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.comparator;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This exception is thrown when something went wrong with the comparator execution.
+ */
+public class ComparatorException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public ComparatorException() {
+ super();
+ }
+
+ public ComparatorException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public ComparatorException(@Nonnull String message) {
+ super(message);
+ }
+
+ public ComparatorException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorFile.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorFile.java
new file mode 100644
index 0000000..9bdcc2c
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorFile.java
@@ -0,0 +1,37 @@
+/*
+ * 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.test.comparator;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+ * {@link Comparator}s of this type are used to compare files.
+ */
+public abstract class ComparatorFile implements Comparator {
+
+ @Nonnull
+ protected File candidate;
+ @Nonnull
+ protected File reference;
+
+ protected ComparatorFile(@Nonnull File candidate, @Nonnull File reference) {
+ this.candidate = candidate;
+ this.reference = reference;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/comparator/ComparatorMapping.java b/jack-tests/src/com/android/jack/test/comparator/ComparatorMapping.java
new file mode 100644
index 0000000..09e78af
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/comparator/ComparatorMapping.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.comparator;
+
+import com.android.jack.DifferenceFoundException;
+import com.android.jack.shrob.ListingComparator;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link Comparator} is used to compare shrob mappings.
+ */
+public class ComparatorMapping extends ComparatorFile {
+
+ public ComparatorMapping(@Nonnull File candidate, @Nonnull File reference) {
+ super(candidate, reference);
+ }
+
+ @Override
+ public void compare() throws DifferenceFoundException, ComparatorException {
+ try {
+ ListingComparator.compare(reference, candidate);
+ } catch (IOException e) {
+ throw new ComparatorException(e);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/CheckDexStructureTestHelper.java b/jack-tests/src/com/android/jack/test/helper/CheckDexStructureTestHelper.java
new file mode 100644
index 0000000..f3858bf
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/CheckDexStructureTestHelper.java
@@ -0,0 +1,37 @@
+/*
+ * 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.test.helper;
+
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link SourceToDexComparisonTestHelper} is used to compare dex files structures.
+ */
+public class CheckDexStructureTestHelper extends SourceToDexComparisonTestHelper {
+
+ public CheckDexStructureTestHelper(@Nonnull File fileOrSourceList) throws Exception {
+ super(fileOrSourceList);
+ }
+
+ public void compare() throws Exception {
+ runTest(createDexFileComparator());
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/ErrorTestHelper.java b/jack-tests/src/com/android/jack/test/helper/ErrorTestHelper.java
new file mode 100644
index 0000000..d7142f5
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/ErrorTestHelper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.helper;
+
+import com.android.jack.test.toolchain.AbstractTestTools;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class is used by error tests, it basically handles the directory structure.
+ */
+public class ErrorTestHelper {
+
+ @Nonnull
+ private final File testingFolder;
+ @Nonnull
+ private final File sourceFolder;
+ @Nonnull
+ private final File jackFolder;
+ @Nonnull
+ private final File outputDexFolder;
+
+ public ErrorTestHelper() throws IOException {
+ this.testingFolder = AbstractTestTools.createTempDir();
+ this.sourceFolder = new File(testingFolder, "src");
+ if (!this.sourceFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + this.sourceFolder.getPath());
+ }
+ this.jackFolder = new File(testingFolder, "jack");
+ if (!this.jackFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + this.jackFolder.getPath());
+ }
+ this.outputDexFolder = new File(this.testingFolder, "dex");
+ if (!this.outputDexFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + this.outputDexFolder.getPath());
+ }
+ }
+
+ @Nonnull
+ public File getSourceFolder() {
+ return sourceFolder;
+ }
+
+ @Nonnull
+ public File getJackFolder() {
+ return jackFolder;
+ }
+
+ @Nonnull
+ public File getTestingFolder() {
+ return testingFolder;
+ }
+
+ @Nonnull
+ public File getOutputDexFolder() {
+ return outputDexFolder;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/GenericComparisonTestHelper.java b/jack-tests/src/com/android/jack/test/helper/GenericComparisonTestHelper.java
new file mode 100644
index 0000000..0b947ee
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/GenericComparisonTestHelper.java
@@ -0,0 +1,45 @@
+/*
+ * 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.test.helper;
+
+import com.android.jack.test.comparator.Comparator;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class implements a pattern of tests where two compilers are used on the same
+ * sources and the result is then compared with a variety of {@link Comparator}s.
+ */
+public abstract class GenericComparisonTestHelper {
+
+ @Nonnull
+ protected abstract void executeCandidateToolchain() throws Exception;
+ @Nonnull
+ protected abstract void executeReferenceToolchain() throws Exception;
+
+ public final void runTest(@Nonnull Comparator... comparators) throws Exception {
+
+ assert comparators.length > 0 : "You must provide at least one comparator";
+
+ executeCandidateToolchain();
+ executeReferenceToolchain();
+
+ for (Comparator comparator : comparators) {
+ comparator.compare();
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/IncrementalTestHelper.java b/jack-tests/src/com/android/jack/test/helper/IncrementalTestHelper.java
new file mode 100644
index 0000000..4f5e172
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/IncrementalTestHelper.java
@@ -0,0 +1,169 @@
+/*
+ * 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.test.helper;
+
+import com.android.jack.backend.jayce.JayceFileImporter;
+import com.android.jack.test.runner.DalvikRunnerHost;
+import com.android.jack.test.runner.RuntimeRunnerFactory;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.JackBasedToolchain;
+import com.android.jack.test.toolchain.JillBasedToolchain;
+
+import junit.framework.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class is used to write tests on incremental compilation.
+ */
+public class IncrementalTestHelper {
+
+ @Nonnull
+ private final File testingFolder;
+ @Nonnull
+ private final File sourceFolder;
+ @Nonnull
+ private final File dexOutDir;
+ @Nonnull
+ private final File dexFile;
+ @Nonnull
+ private final File compilerStateFolder;
+ @Nonnull
+ private final File jackFolder;
+ @Nonnull
+ private final Set<File> javaFiles = new HashSet<File>();
+ @Nonnull
+ private final Map<String, Long> fileModificationDate = new HashMap<String, Long>();
+
+ public IncrementalTestHelper(@Nonnull File testingFolder) throws IOException {
+ this.testingFolder = testingFolder;
+ this.sourceFolder = new File(testingFolder, "src");
+ if (!this.sourceFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + this.sourceFolder.getAbsolutePath());
+ }
+ compilerStateFolder = new File(testingFolder, "compileState");
+ if (!compilerStateFolder.exists() && !compilerStateFolder.mkdir()) {
+ throw new IOException("Failed to create folder " + compilerStateFolder.getAbsolutePath());
+ }
+ dexOutDir = new File(testingFolder, "outputBinary");
+ if (!dexOutDir.exists() && !dexOutDir.mkdir()) {
+ throw new IOException("Failed to create folder " + dexOutDir.getAbsolutePath());
+ }
+ dexFile = new File(dexOutDir, "classes.dex");
+ jackFolder = new File(compilerStateFolder, "jackFiles");
+ }
+
+ @Nonnull
+ public File addJavaFile(@Nonnull String packageName, @Nonnull String fileName,
+ @Nonnull String fileContent) throws IOException {
+ File file = AbstractTestTools.createJavaFile(sourceFolder, packageName, fileName, fileContent);
+ javaFiles.add(file);
+ return file;
+ }
+
+ public void deleteJavaFile(@Nonnull File javaFile)
+ throws IOException {
+ AbstractTestTools.deleteFile(javaFile);
+ javaFiles.remove(javaFile);
+ }
+
+ @Nonnull
+ public File getCompilerStateFolder() {
+ return compilerStateFolder;
+ }
+
+ public void cleanSnapshot() {
+ fileModificationDate.clear();
+ }
+
+ public void snapshotJackFilesModificationDate() {
+ List<File> jackFiles = new ArrayList<File>();
+ fillJackFiles(jackFolder, jackFiles);
+ for (File jackFile : jackFiles) {
+ fileModificationDate.put(jackFile.getAbsolutePath(), Long.valueOf(jackFile.lastModified()));
+ }
+ }
+
+ private void fillJackFiles(@Nonnull File file, @Nonnull List<File> jackFiles) {
+ if (file.isDirectory()) {
+ for (File subFile : file.listFiles()) {
+ fillJackFiles(subFile, jackFiles);
+ }
+ } else if (file.getName().endsWith(JayceFileImporter.JAYCE_FILE_EXTENSION)) {
+ jackFiles.add(file);
+ }
+ }
+
+ @Nonnull
+ public List<String> getFQNOfRebuiltTypes() {
+ assert !fileModificationDate.isEmpty();
+
+ List<String> fqnOfRebuiltTypes = new ArrayList<String>();
+ List<File> jackFiles = new ArrayList<File>();
+ fillJackFiles(jackFolder, jackFiles);
+
+ for (File jackFile : jackFiles) {
+ Long previousDate = fileModificationDate.get(jackFile.getAbsolutePath());
+ if (previousDate == null || jackFile.lastModified() > previousDate.longValue()) {
+ String jackFileName = jackFile.getAbsolutePath();
+ String binaryTypeName = jackFileName.substring(0, jackFileName.indexOf(".jack"));
+ binaryTypeName = binaryTypeName.substring(jackFolder.getAbsolutePath().length() + 1);
+ fqnOfRebuiltTypes.add(binaryTypeName.replace(File.separatorChar, '.'));
+ }
+ }
+
+ return (fqnOfRebuiltTypes);
+ }
+
+ public void incrementalBuildFromFolder() throws Exception {
+ JackBasedToolchain jackToolchain =
+ AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class, JillBasedToolchain.class);
+ jackToolchain.setIncrementalFolder(getCompilerStateFolder());
+
+ jackToolchain.srcToExe(
+ AbstractTestTools.getClasspathAsString(jackToolchain.getDefaultBootClasspath()), dexOutDir,
+ sourceFolder);
+
+ Thread.sleep(1000);
+ }
+
+ @Nonnull
+ public String run(@Nonnull String mainClass) throws Exception {
+ DalvikRunnerHost runner =
+ (DalvikRunnerHost) RuntimeRunnerFactory.create("dalvik-fast-host");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ runner.setOutputStream(out);
+ Assert.assertEquals(0, runner.run(new String [0], new String[] {mainClass}, dexFile));
+ return out.toString();
+ }
+
+ @Nonnull
+ public List<File> getJackFiles() {
+ return AbstractTestTools.getFiles(jackFolder, ".jack");
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/JackDexMergerTestHelper.java b/jack-tests/src/com/android/jack/test/helper/JackDexMergerTestHelper.java
new file mode 100644
index 0000000..b9d8669
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/JackDexMergerTestHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.test.helper;
+
+import com.android.jack.Options;
+import com.android.jack.TestTools;
+import com.android.jack.backend.dex.rop.CodeItemBuilder;
+import com.android.jack.test.comparator.Comparator;
+import com.android.jack.test.comparator.ComparatorComposite;
+import com.android.jack.test.comparator.ComparatorDex;
+import com.android.jack.test.comparator.ComparatorDexAnnotations;
+import com.android.jack.test.comparator.ComparatorDiff;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.JackBasedToolchain;
+import com.android.sched.scheduler.ScheduleInstance;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class defines a helper for dex merger tests.
+ */
+public class JackDexMergerTestHelper extends SourceToDexComparisonTestHelper {
+
+ public JackDexMergerTestHelper(@Nonnull File fileOrSourceList) throws Exception {
+ super(fileOrSourceList);
+ }
+
+ @Override
+ @Nonnull
+ protected JackBasedToolchain getCandidateToolchain() {
+ // Mono dex
+ JackBasedToolchain toolchain =
+ AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class);
+ toolchain.addProperty(Options.EMIT_LINE_NUMBER_DEBUG_INFO.getName(),
+ Boolean.toString(withDebugInfos));
+ toolchain.addProperty(ScheduleInstance.DEFAULT_RUNNER.getName(), "single-threaded");
+ toolchain.addProperty(CodeItemBuilder.FORCE_JUMBO.getName(), "true");
+
+ return toolchain;
+ }
+
+ @Override
+ @Nonnull
+ protected JackBasedToolchain getReferenceToolchain() {
+ // One dex per type
+ JackBasedToolchain toolchain =
+ AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class);
+ File oneDexPerTypeFolder;
+ try {
+ oneDexPerTypeFolder = TestTools.createTempDir("oneDexPerType", "dex");
+ toolchain.addProperty(Options.EMIT_LINE_NUMBER_DEBUG_INFO.getName(),
+ Boolean.toString(withDebugInfos));
+ toolchain.addProperty(ScheduleInstance.DEFAULT_RUNNER.getName(), "single-threaded");
+ toolchain.addProperty(Options.TYPEDEX_DIR.getName(),
+ oneDexPerTypeFolder.getAbsolutePath());
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ return toolchain;
+ }
+
+ @Override
+ @Nonnull
+ public Comparator createDexFileComparator() {
+ ComparatorDex comparatorDex = new ComparatorDex(candidateDex, refDex);
+ comparatorDex.setWithDebugInfo(false);
+ comparatorDex.setStrict(true);
+ comparatorDex.setCompareDebugInfoBinary(false);
+ comparatorDex.setCompareInstructionNumber(true);
+ comparatorDex.setInstructionNumberTolerance(0);
+ ComparatorDexAnnotations comparatorAnnotations =
+ new ComparatorDexAnnotations(candidateDex, refDex);
+ ComparatorDiff comparatorDiff = new ComparatorDiff(candidateDex, refDex);
+ return new ComparatorComposite(comparatorDex, comparatorAnnotations, comparatorDiff);
+ }
+
+ public void compare() throws Exception {
+ runTest(createDexFileComparator());
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/RuntimeTestHelper.java b/jack-tests/src/com/android/jack/test/helper/RuntimeTestHelper.java
new file mode 100644
index 0000000..2923699
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/RuntimeTestHelper.java
@@ -0,0 +1,373 @@
+/*
+ * 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.test.helper;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+
+import com.android.jack.test.runner.RuntimeRunner;
+import com.android.jack.test.runtime.RuntimeTestInfo;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.AndroidToolchain;
+import com.android.jack.test.toolchain.Toolchain.SourceLevel;
+import com.android.sched.util.collect.Lists;
+
+import junit.framework.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * This class is used to write runtime tests.
+ */
+public class RuntimeTestHelper {
+
+ @Nonnull
+ private AndroidToolchain candidateTestTools;
+ @Nonnull
+ private AndroidToolchain referenceTestTools;
+
+ @Nonnull
+ private List<File> baseDirs = new ArrayList<File>(1);
+ @Nonnull
+ private List<String> jUnitClasses = new ArrayList<String>(1);
+
+ @Nonnull
+ private String srcDirName = "jack";
+ @Nonnull
+ private String libDirName = "lib";
+ @Nonnull
+ private String refDirName = "dx";
+ @Nonnull
+ private String linkDirName = "link";
+
+ private boolean withDebugInfos = false;
+
+ @CheckForNull
+ private String jarjarRulesFileName;
+ @CheckForNull
+ private String[] proguardFlagsFileNames;
+
+ @Nonnull
+ private String propertyFileName = "test.properties";
+
+ {
+ candidateTestTools = AbstractTestTools.getCandidateToolchain(AndroidToolchain.class);
+ referenceTestTools = AbstractTestTools.getReferenceToolchain(AndroidToolchain.class);
+ }
+
+ public RuntimeTestHelper(@Nonnull RuntimeTestInfo... rtTestInfos) {
+ for (RuntimeTestInfo info : rtTestInfos) {
+ baseDirs.add(info.directory);
+ jUnitClasses.add(info.jUnit);
+ }
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setSrcDirName(@Nonnull String srcDirName) {
+ this.srcDirName = srcDirName;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setLibDirName(@Nonnull String libDirName) {
+ this.libDirName = libDirName;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setRefDirName(@Nonnull String refDirName) {
+ this.refDirName = refDirName;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setLinkDirName(@Nonnull String linkDirName) {
+ this.linkDirName = linkDirName;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setWithDebugInfos(boolean withDebugInfos) {
+ this.withDebugInfos = withDebugInfos;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setSourceLevel(@Nonnull SourceLevel level) {
+ candidateTestTools.setSourceLevel(level);
+ referenceTestTools.setSourceLevel(level);
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setPropertyFileName(@Nonnull String propertyFileName) {
+ this.propertyFileName = propertyFileName;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setJarjarRulesFileName(@Nonnull String name) {
+ this.jarjarRulesFileName = name;
+ return this;
+ }
+
+ @Nonnull
+ public RuntimeTestHelper setProguardFlagsFileNames(@Nonnull String[] proguardFlagsFileNames) {
+ this.proguardFlagsFileNames = proguardFlagsFileNames;
+ return this;
+ }
+
+ public void compileAndRunTest() throws Exception {
+ Properties testProperties = new Properties();
+ try {
+ loadTestProperties(testProperties);
+ } catch (FileNotFoundException e) {
+ // No file, no pb
+ }
+
+ candidateTestTools.setWithDebugInfos(withDebugInfos);
+ referenceTestTools.setWithDebugInfos(withDebugInfos);
+
+ File[] candidateBootClasspath = candidateTestTools.getDefaultBootClasspath();
+ File[] referenceBootClasspath = referenceTestTools.getDefaultBootClasspath();
+
+ String candidateBootClasspathAsString =
+ AbstractTestTools.getClasspathAsString(candidateTestTools.getDefaultBootClasspath());
+ String referenceBootClasspathAsString =
+ AbstractTestTools.getClasspathAsString(referenceTestTools.getDefaultBootClasspath());
+
+ // Compile lib src
+ File libLibRef = null;
+ File libBinaryRef = null;
+ File libLibCandidate = null;
+ if (getLibSrc().length != 0) {
+ libLibRef =
+ AbstractTestTools.createTempFile("-lib-ref", referenceTestTools.getLibraryExtension());
+ File libBinaryRefDir = AbstractTestTools.createTempDir();
+ libBinaryRef = new File(libBinaryRefDir, referenceTestTools.getBinaryFileName());
+ referenceTestTools.srcToLib(referenceBootClasspathAsString, libLibRef, /* zipFiles = */true,
+ getLibSrc());
+ referenceTestTools.libToDex(libLibRef, libBinaryRefDir);
+
+ libLibCandidate = AbstractTestTools.createTempFile("-lib-candidate",
+ candidateTestTools.getLibraryExtension());
+ candidateTestTools.srcToLib(candidateBootClasspathAsString, libLibCandidate,
+ /* zipFiles = */true, getLibSrc());
+ }
+
+ // Compile test src
+ String candidateClasspathAsString;
+ String referenceClasspathAsString;
+ if (getLibSrc().length != 0) {
+ File[] candidateClassPath = new File[candidateBootClasspath.length + 1];
+ System.arraycopy(candidateBootClasspath, 0, candidateClassPath, 0,
+ candidateBootClasspath.length);
+ candidateClassPath[candidateClassPath.length - 1] = libLibRef;
+ candidateClasspathAsString = AbstractTestTools.getClasspathAsString(candidateClassPath);
+ File[] referenceClasspath = new File[referenceBootClasspath.length + 1];
+ System.arraycopy(referenceBootClasspath, 0, referenceClasspath, 0,
+ referenceBootClasspath.length);
+ referenceClasspath[referenceClasspath.length - 1] = libLibRef;
+ referenceClasspathAsString = AbstractTestTools.getClasspathAsString(referenceClasspath);
+ } else {
+ candidateClasspathAsString = candidateBootClasspathAsString;
+ referenceClasspathAsString = referenceBootClasspathAsString;
+ }
+
+ File jarjarRules = getJarjarRules();
+ List<File> proguargFlags = getProguardFlags();
+
+ File testBinaryDir = AbstractTestTools.createTempDir();
+ File testBinary = new File(testBinaryDir, candidateTestTools.getBinaryFileName());
+ if (jarjarRules != null) {
+ candidateTestTools.setJarjarRules(jarjarRules);
+ }
+ candidateTestTools.addProguardFlags(proguargFlags.toArray(new File [proguargFlags.size()]));
+ candidateTestTools.srcToExe(candidateClasspathAsString, testBinaryDir, getSrcDir());
+
+ File testLib =
+ AbstractTestTools.createTempFile("testRef", referenceTestTools.getLibraryExtension());
+ referenceTestTools.srcToLib(referenceClasspathAsString, testLib, /* zipFiles = */true,
+ getSrcDir());
+
+ // Compile link src
+ File linkBinary = null;
+ if (getLinkSrc().length != 0) {
+ File linkBinaryDir = AbstractTestTools.createTempDir();
+ linkBinary = new File(linkBinaryDir, candidateTestTools.getBinaryFileName());
+ candidateTestTools.setJarjarRules(jarjarRules);
+ candidateTestTools.addProguardFlags(proguargFlags.toArray(new File [proguargFlags.size()]));
+ candidateTestTools.srcToExe(candidateBootClasspathAsString, linkBinaryDir, getLinkSrc());
+ }
+
+ // Compile ref part src
+ List<File> referenceClasspath = new ArrayList<File>();
+ for (File f : referenceBootClasspath) {
+ referenceClasspath.add(f);
+ }
+ if (libLibRef != null) {
+ referenceClasspath.add(libLibRef);
+ }
+ if (testLib != null) {
+ referenceClasspath.add(testLib);
+ }
+ referenceClasspathAsString = AbstractTestTools.getClasspathAsString(
+ referenceClasspath.toArray(new File[referenceClasspath.size()]));
+
+ File refPartBinaryDir = AbstractTestTools.createTempDir();
+ File refPartBinary = new File(refPartBinaryDir, referenceTestTools.getBinaryFileName());
+ referenceTestTools.srcToExe(referenceClasspathAsString, refPartBinaryDir, getRefSrcDir());
+
+ List<File> rtClasspath = new ArrayList<File>();
+ rtClasspath.add(new File(AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/prebuilts/core-hostdex.jar"));
+ rtClasspath.add(new File(AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/prebuilts/junit4-hostdex.jar"));
+ if (refPartBinary != null) {
+ rtClasspath.add(refPartBinary);
+ }
+ if (linkBinary != null) {
+ rtClasspath.add(linkBinary);
+ }
+ if (testBinary != null) {
+ rtClasspath.add(testBinary);
+ }
+ if (libBinaryRef != null) {
+ rtClasspath.add(libBinaryRef);
+ }
+
+ // Run JUnit on runtime env(s)
+ runOnRuntimeEnvironments(jUnitClasses, testProperties,
+ rtClasspath.toArray(new File[rtClasspath.size()]));
+ }
+
+ private static void runOnRuntimeEnvironments(@Nonnull List<String> jUnitClasses,
+ @Nonnull Properties testProperties, @Nonnull File... classpathFiles) throws Exception {
+ List<RuntimeRunner> runnerList = AbstractTestTools.listRuntimeTestRunners(testProperties);
+ for (RuntimeRunner runner : runnerList) {
+ Assert.assertEquals(0, runner.run(
+ getRuntimeArgs(runner.getClass().getSimpleName(), testProperties), Lists.add(jUnitClasses,
+ 0, AbstractTestTools.JUNIT_RUNNER_NAME).toArray(new String[jUnitClasses.size() + 1]),
+ classpathFiles));
+ }
+ }
+
+ @Nonnull
+ private static final String[] getRuntimeArgs(@Nonnull String rtName,
+ @Nonnull Properties properties) {
+
+ String args = properties.getProperty("rt.args." + rtName);
+ List<String> result = new ArrayList<String>(0);
+ if (args != null) {
+ for (String arg : Splitter.on(CharMatcher.WHITESPACE).split(args)) {
+ result.add(arg);
+ }
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ private void loadTestProperties(@Nonnull Properties properties) throws FileNotFoundException,
+ IOException {
+ File[] propertyFile = getDirectoryOrFile(propertyFileName);
+ if (propertyFile.length != 0) {
+ if (baseDirs.size() > 1) {
+ throw new AssertionError("Non regression test found");
+ }
+ if (propertyFile[0].exists()) {
+ properties.load(new FileInputStream(propertyFile[0]));
+ }
+ }
+ }
+
+ @Nonnull
+ private File[] getSrcDir() {
+ return getDirectoryOrFile(srcDirName);
+ }
+
+ @Nonnull
+ private File[] getLibSrc() {
+ return getDirectoryOrFile(libDirName);
+ }
+
+ @Nonnull
+ private File[] getLinkSrc() {
+ File[] result = getDirectoryOrFile(linkDirName);
+ if (result.length != 0 && baseDirs.size() > 1) {
+ throw new AssertionError("Not a regression test");
+ }
+ return result;
+ }
+
+ @Nonnull
+ private File[] getRefSrcDir() {
+ return getDirectoryOrFile(refDirName);
+ }
+
+ @CheckForNull
+ private File getJarjarRules() {
+ File[] result = getDirectoryOrFile(jarjarRulesFileName);
+ if (result.length != 0) {
+ if (baseDirs.size() > 1) {
+ throw new AssertionError("Not a regression test");
+ }
+ return result[0];
+ }
+ return null;
+ }
+
+ @Nonnull
+ private List<File> getProguardFlags() {
+ List<File> result = new ArrayList<File>();
+ if (proguardFlagsFileNames != null) {
+ for (String s : proguardFlagsFileNames) {
+ File[] f = getDirectoryOrFile(s);
+ if (f.length != 0 && f[0].exists()) {
+ result.add(f[0]);
+ }
+ }
+ }
+
+ if (!result.isEmpty() && baseDirs.size() > 1) {
+ throw new AssertionError("Not a regression test");
+ }
+
+ return result;
+ }
+
+ @Nonnull
+ private File[] getDirectoryOrFile(@CheckForNull String name) {
+ List<File> result = new ArrayList<File>();
+ if (name != null) {
+ for (File f : baseDirs) {
+ File absFile = new File(f, name);
+ if (absFile.exists()) {
+ result.add(absFile);
+ }
+ }
+ }
+ return result.toArray(new File[result.size()]);
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/helper/SourceToDexComparisonTestHelper.java b/jack-tests/src/com/android/jack/test/helper/SourceToDexComparisonTestHelper.java
new file mode 100644
index 0000000..72d9108
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/helper/SourceToDexComparisonTestHelper.java
@@ -0,0 +1,166 @@
+/*
+ * 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.test.helper;
+
+import com.android.jack.test.comparator.Comparator;
+import com.android.jack.test.comparator.ComparatorDex;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.AndroidToolchain;
+
+import java.io.File;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * This class is a {@link GenericComparisonTestHelper} where the two compilers perform
+ * a source-to-dex compilation.
+ */
+public class SourceToDexComparisonTestHelper extends GenericComparisonTestHelper {
+
+ @Nonnull
+ private File candidateDexDir;
+ @Nonnull
+ protected File candidateDex;
+ @Nonnull
+ private File refDexDir;
+ @Nonnull
+ protected File refDex;
+
+ @Nonnull
+ private File[] candidateClasspath;
+ @Nonnull
+ private File[] referenceClasspath;
+
+ @Nonnull
+ private File fileOrSourceList;
+
+ @CheckForNull
+ private File jarjarRulesFile = null;
+ @Nonnull
+ private File[] proguardFlagFiles = new File[0];
+
+ @Nonnull
+ private AndroidToolchain candidateTestTools;
+ @Nonnull
+ private AndroidToolchain referenceTestTools;
+
+ protected boolean withDebugInfos = false;
+
+ public SourceToDexComparisonTestHelper(@Nonnull File fileOrSourceList) throws Exception {
+
+ this.fileOrSourceList = fileOrSourceList;
+
+ candidateTestTools = getCandidateToolchain();
+ referenceTestTools = getReferenceToolchain();
+
+ candidateClasspath = candidateTestTools.getDefaultBootClasspath();
+ referenceClasspath = referenceTestTools.getDefaultBootClasspath();
+
+ candidateDexDir = AbstractTestTools.createTempDir();
+ refDexDir = AbstractTestTools.createTempDir();
+
+ candidateDex = new File(candidateDexDir, candidateTestTools.getBinaryFileName());
+ refDex = new File(refDexDir, referenceTestTools.getBinaryFileName());
+ }
+
+ @Nonnull
+ protected AndroidToolchain getCandidateToolchain() {
+ return AbstractTestTools.getCandidateToolchain(AndroidToolchain.class);
+ }
+
+ @Nonnull
+ protected AndroidToolchain getReferenceToolchain() {
+ return AbstractTestTools.getReferenceToolchain(AndroidToolchain.class);
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setCandidateTestTools(
+ @Nonnull AndroidToolchain candidateTestTools) {
+ this.candidateTestTools = candidateTestTools;
+ return this;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setReferenceTestTools(
+ @Nonnull AndroidToolchain referenceTestTools) {
+ this.referenceTestTools = referenceTestTools;
+ return this;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setCandidateClasspath(@Nonnull File[] classpath) {
+ candidateClasspath = classpath;
+ return this;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setReferenceClasspath(@Nonnull File[] classpath) {
+ referenceClasspath = classpath;
+ return this;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setWithDebugInfo(boolean withDebugInfo) {
+ this.withDebugInfos = withDebugInfo;
+ return this;
+ }
+
+ @Nonnull
+ public Comparator createDexFileComparator() {
+ ComparatorDex comparator = new ComparatorDex(candidateDex, refDex);
+ comparator.setWithDebugInfo(withDebugInfos);
+ comparator.setStrict(false);
+ comparator.setCompareDebugInfoBinary(false);
+ comparator.setCompareInstructionNumber(false);
+ comparator.setInstructionNumberTolerance(0f);
+ return comparator;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setJarjarRulesFile(@Nonnull File jarjarRulesFile) {
+ this.jarjarRulesFile = jarjarRulesFile;
+ return this;
+ }
+
+ @Nonnull
+ public SourceToDexComparisonTestHelper setProguardFlags(@Nonnull File[] proguardFlags) {
+ this.proguardFlagFiles = proguardFlags;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ protected void executeCandidateToolchain() throws Exception {
+ if (jarjarRulesFile != null) {
+ candidateTestTools.setJarjarRules(jarjarRulesFile);
+ }
+ candidateTestTools.addProguardFlags(proguardFlagFiles).srcToExe(
+ AbstractTestTools.getClasspathAsString(candidateClasspath), candidateDexDir,
+ fileOrSourceList);
+ }
+
+ @Override
+ @Nonnull
+ protected void executeReferenceToolchain() throws Exception {
+ if (jarjarRulesFile != null) {
+ referenceTestTools.setJarjarRules(jarjarRulesFile);
+ }
+ referenceTestTools.addProguardFlags(proguardFlagFiles).srcToExe(
+ AbstractTestTools.getClasspathAsString(referenceClasspath), refDexDir, fileOrSourceList);
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/AbstractRuntimeRunner.java b/jack-tests/src/com/android/jack/test/runner/AbstractRuntimeRunner.java
new file mode 100644
index 0000000..a20ff93
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/AbstractRuntimeRunner.java
@@ -0,0 +1,73 @@
+/*
+ * 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.test.runner;
+
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import javax.annotation.Nonnull;
+
+
+/**
+ * This {@link RuntimeRunner} can have its outputs redirected in user defined streams.
+ */
+public abstract class AbstractRuntimeRunner extends RuntimeRunner {
+
+ @Nonnull
+ protected PrintStream outRedirectStream = System.out;
+ @Nonnull
+ protected PrintStream errRedirectStream = System.err;
+
+ protected AbstractRuntimeRunner(@Nonnull File rtEnvRootDir) {
+ super(rtEnvRootDir);
+ }
+
+ @Override
+ public abstract int run(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException;
+
+ @Nonnull
+ public final AbstractRuntimeRunner setOutputStream(@Nonnull OutputStream outputStream) {
+ if (outRedirectStream != null) {
+ outRedirectStream.close();
+ }
+ outRedirectStream = new PrintStream(outputStream);
+ return this;
+ }
+
+ @Nonnull
+ public final AbstractRuntimeRunner setErrorStream(@Nonnull OutputStream errorStream) {
+ if (errRedirectStream != null) {
+ errRedirectStream.close();
+ }
+ errRedirectStream = new PrintStream(errorStream);
+ return this;
+ }
+
+ @Nonnull
+ public PrintStream getOutStream() {
+ return outRedirectStream;
+ }
+
+ @Nonnull
+ public PrintStream getErrStream() {
+ return errRedirectStream;
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/ArtRunnerDevice.java b/jack-tests/src/com/android/jack/test/runner/ArtRunnerDevice.java
new file mode 100644
index 0000000..e93e20b
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/ArtRunnerDevice.java
@@ -0,0 +1,70 @@
+/*
+ * 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.test.runner;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link RuntimeRunner} is used to run tests on art running on device.
+ */
+public class ArtRunnerDevice extends DeviceRunner {
+
+ @Override
+ public int run(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException {
+ return runOnDevice(options, mainClasses, classpathFiles);
+ }
+
+ @Override
+ @Nonnull
+ protected List<String> buildCommandLine(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) {
+ List<String> args = new ArrayList<String>();
+
+ args.add(rtEnvironmentRootDir.getAbsolutePath() + "/bin/dalvikvm");
+
+ for (String option : options) {
+ args.add(option);
+ }
+
+ args.add("-classpath");
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < classpathFiles.length; i++) {
+ if (i > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(classpathFiles[i].getAbsolutePath());
+ }
+ args.add(sb.toString());
+
+ for (String className : mainClasses) {
+ args.add(className);
+ }
+ return args;
+ }
+
+ @Override
+ @Nonnull
+ protected String getRuntimeName() {
+ return "ART";
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/ArtRunnerHost.java b/jack-tests/src/com/android/jack/test/runner/ArtRunnerHost.java
new file mode 100644
index 0000000..c9a46fb
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/ArtRunnerHost.java
@@ -0,0 +1,66 @@
+/*
+ * 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.test.runner;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link RuntimeRunner} is used to run tests on art running on host.
+ */
+public class ArtRunnerHost extends HostRunner {
+
+ public ArtRunnerHost(@Nonnull File rtEnvironmentRootDir) {
+ super(rtEnvironmentRootDir);
+ }
+
+ @Override
+ public int run(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException {
+ return runOnHost(buildCommandLine(options, mainClasses, classpathFiles), "ANDROID_HOST_OUT");
+ }
+
+ @Nonnull
+ private List<String> buildCommandLine(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) {
+ List<String> args = new ArrayList<String>();
+
+ args.add(rtEnvironmentRootDir.getAbsolutePath() + "/bin/art");
+
+ for (String option : options) {
+ args.add(option);
+ }
+
+ args.add("-classpath");
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < classpathFiles.length; i++) {
+ if (i > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(classpathFiles[i].getAbsolutePath());
+ }
+ args.add(sb.toString());
+
+ for (String className : mainClasses) {
+ args.add(className);
+ }
+ return args;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/DalvikRunner.java b/jack-tests/src/com/android/jack/test/runner/DalvikRunner.java
new file mode 100644
index 0000000..e905394
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/DalvikRunner.java
@@ -0,0 +1,49 @@
+/*
+ * 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.test.runner;
+
+import javax.annotation.Nonnull;
+
+
+/**
+ * This interface defines options that can be set to a Dalvik runner.
+ */
+public interface DalvikRunner {
+
+ /**
+ * This enum defines various mode Dalvik can be run into.
+ */
+ enum DalvikMode {
+ JIT("-Xint:jit"),
+ FAST("-Xint:fast");
+
+ @Nonnull
+ String arg;
+
+ DalvikMode(@Nonnull String arg) {
+ this.arg = arg;
+ }
+
+ @Nonnull
+ String getArg() {
+ return arg;
+ }
+ }
+
+ @Nonnull
+ DalvikRunner setMode(@Nonnull DalvikMode mode);
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/DalvikRunnerDevice.java b/jack-tests/src/com/android/jack/test/runner/DalvikRunnerDevice.java
new file mode 100644
index 0000000..c849189
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/DalvikRunnerDevice.java
@@ -0,0 +1,81 @@
+/*
+ * 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.test.runner;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link RuntimeRunner} is used to run tests on dalvik running on device.
+ */
+public class DalvikRunnerDevice extends DeviceRunner implements DalvikRunner {
+
+ @Nonnull
+ private DalvikMode mode = DalvikMode.FAST;
+
+ @Override
+ public int run(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException {
+ return runOnDevice(options, mainClasses, classpathFiles);
+ }
+
+ @Override
+ @Nonnull
+ protected List<String> buildCommandLine(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) {
+ List<String> args = new ArrayList<String>();
+
+ args.add(rtEnvironmentRootDir.getAbsolutePath() + "/bin/dalvikvm");
+
+ args.add(mode.getArg());
+
+ for (String option : options) {
+ args.add(option);
+ }
+
+ args.add("-classpath");
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < classpathFiles.length; i++) {
+ if (i > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(classpathFiles[i].getAbsolutePath());
+ }
+ args.add(sb.toString());
+
+ for (String className : mainClasses) {
+ args.add(className);
+ }
+ return args;
+ }
+
+ @Override
+ @Nonnull
+ public DalvikRunnerDevice setMode(@Nonnull DalvikMode mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ protected String getRuntimeName() {
+ return "DalvikVM";
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/DalvikRunnerHost.java b/jack-tests/src/com/android/jack/test/runner/DalvikRunnerHost.java
new file mode 100644
index 0000000..7ada39c
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/DalvikRunnerHost.java
@@ -0,0 +1,78 @@
+/*
+ * 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.test.runner;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link RuntimeRunner} is used to run tests on dalvik running on host.
+ */
+public class DalvikRunnerHost extends HostRunner implements DalvikRunner {
+
+ @Nonnull
+ private DalvikMode mode = DalvikMode.FAST;
+
+ public DalvikRunnerHost(@Nonnull File rtEnvironmentRootDir) {
+ super(rtEnvironmentRootDir);
+ }
+
+ @Override
+ public int run(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException {
+ return runOnHost(buildCommandLine(options, mainClasses, classpathFiles), "ANDROID_ROOT");
+ }
+
+ @Nonnull
+ private List<String> buildCommandLine(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles) {
+ List<String> args = new ArrayList<String>();
+
+ args.add(rtEnvironmentRootDir.getAbsolutePath() + "/bin/dalvik");
+
+ args.add(mode.getArg());
+
+ for (String option : options) {
+ args.add(option);
+ }
+
+ args.add("-classpath");
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < classpathFiles.length; i++) {
+ if (i > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(classpathFiles[i].getAbsolutePath());
+ }
+ args.add(sb.toString());
+
+ for (String className : mainClasses) {
+ args.add(className);
+ }
+ return args;
+ }
+
+ @Override
+ @Nonnull
+ public DalvikRunnerHost setMode(@Nonnull DalvikMode mode) {
+ this.mode = mode;
+ return this;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java b/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java
new file mode 100644
index 0000000..dbb9203
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java
@@ -0,0 +1,336 @@
+/*
+ * 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.test.runner;
+
+import com.google.common.base.Joiner;
+
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.TestConfigurationException;
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This runner is used to execute tests on a device.
+ */
+public abstract class DeviceRunner extends AbstractRuntimeRunner {
+
+ @Nonnull
+ public static final File ROOT_DIR = new File("/system");
+ @Nonnull
+ public static final File ANDROID_DATA_DIR = new File("/data");
+
+ private static final long ADB_CONNECTION_TIMEOUT = 5000;
+ private static final long ADB_WAIT_STEP = ADB_CONNECTION_TIMEOUT / 10;
+
+ @Nonnull
+ private MyShellOuputReceiver shellOutput = new MyShellOuputReceiver();
+
+ private class MyShellOuputReceiver implements IShellOutputReceiver {
+
+ @Override
+ public void addOutput(@Nonnull byte[] data, int offset, int length) {
+ outRedirectStream.println(new String(Arrays.copyOfRange(data, offset, offset + length)));
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+ }
+
+ public DeviceRunner() {
+ super(ROOT_DIR);
+ try {
+ AndroidDebugBridge.init(/* clientSupport */ false);
+ } catch (IllegalStateException ex) {
+ // ADB was already initialized, we're fine, so just ignore.
+ }
+ }
+
+ private class ShellOutputToStringReceiver implements IShellOutputReceiver {
+
+ @Nonnull
+ StringBuffer out = new StringBuffer();
+
+ @Override
+ public void addOutput(@Nonnull byte[] data, int offset, int length) {
+ out.append(new String(Arrays.copyOfRange(data, offset, offset + length)));
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Nonnull
+ public String getOutput() {
+ return out.toString();
+ }
+ }
+
+ protected int runOnDevice(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles)
+ throws RuntimeRunnerException {
+
+ // Assumes adb is in PATH
+ AndroidDebugBridge adb = AndroidDebugBridge.createBridge("adb", false);
+
+ long start = System.currentTimeMillis();
+
+ if (isVerbose) {
+ outRedirectStream.println("Initializing adb...");
+ }
+
+ while (!isAdbInitialized(adb)) {
+ long timeLeft = start + ADB_CONNECTION_TIMEOUT - System.currentTimeMillis();
+ if (timeLeft <= 0) {
+ break;
+ }
+ try {
+ Thread.sleep(ADB_WAIT_STEP);
+ } catch (InterruptedException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+ if (!isAdbInitialized(adb)) {
+ throw new RuntimeRunnerException("adb is not initialized");
+ }
+
+ if (isVerbose) {
+ outRedirectStream.println("Done");
+ }
+
+ IDevice[] connectedDevices = adb.getDevices();
+
+ if (connectedDevices.length == 0) {
+ throw new RuntimeRunnerException("No device found");
+ }
+
+ int exitStatus = -1;
+ for (IDevice device : connectedDevices) {
+
+ checkDeviceRuntime(device);
+
+ if (isVerbose) {
+ outRedirectStream.println("Running on device: " + device.getName());
+ }
+
+ ensureAdbRoot(device);
+
+ File testsRootDir = new File(device.getMountPoint(IDevice.MNT_DATA) + "/jack-tests");
+ File[] desFilePaths = new File[classpathFiles.length];
+ try {
+ if (isVerbose) {
+ outRedirectStream.println("adb shell -s " + device.getSerialNumber() + " mkdir "
+ + testsRootDir.getAbsolutePath());
+ }
+ device.executeShellCommand("mkdir " + testsRootDir.getAbsolutePath(), shellOutput);
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " push "
+ + System.getProperty("user.dir") + File.separator + "test-exit-status.sh "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+ }
+ device.pushFile(System.getProperty("user.dir") + File.separator + "test-exit-status.sh",
+ testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " shell chmod 777 "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+ }
+ device.executeShellCommand(
+ "chmod 777 " + testsRootDir.getAbsolutePath() + "/test-exit-status.sh", shellOutput);
+
+ int i = 0;
+ for (File f : classpathFiles) {
+ desFilePaths[i] = new File(testsRootDir, "f" + i + "_" + f.getName());
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " push "
+ + f.getAbsolutePath() + " " + desFilePaths[i].getAbsolutePath());
+ }
+ device.pushFile(f.getAbsolutePath(), desFilePaths[i].getAbsolutePath());
+ i++;
+ }
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (SyncException e) {
+ throw new RuntimeRunnerException(e);
+ }
+
+ String args = Joiner.on(' ').join(buildCommandLine(options, mainClasses, desFilePaths));
+
+ try {
+ // Bug : exit code return by adb shell is wrong (always 0)
+ // https://code.google.com/p/android/issues/detail?id=3254
+ // Use go team hack to work this around
+ // https://code.google.com/p/go/source/browse/misc/arm/a
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " shell "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh " + args);
+ }
+ device.executeShellCommand(
+ testsRootDir.getAbsolutePath() + "/test-exit-status.sh " + args,
+ shellOutput);
+
+ File exitStatusFile = AbstractTestTools.createTempFile("exitStatus", "");
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " pull "
+ + testsRootDir.getAbsolutePath() + "/exitStatus " + exitStatusFile.getAbsolutePath());
+ }
+ device.pullFile(testsRootDir.getAbsolutePath() + "/exitStatus",
+ exitStatusFile.getAbsolutePath());
+
+ BufferedReader br = new BufferedReader(new FileReader(exitStatusFile));
+ try {
+ String readLine = br.readLine();
+ if (readLine == null) {
+ throw new RuntimeRunnerException("Exit status not found");
+ }
+ exitStatus = Integer.parseInt(readLine);
+ } finally {
+ br.close();
+ }
+
+ if (isVerbose) {
+ outRedirectStream.println("Exit status: " + exitStatus);
+ }
+
+ for (File pushedFile : desFilePaths) {
+ if (isVerbose) {
+ outRedirectStream.println(
+ "adb -s " + device.getSerialNumber() + "rm " + pushedFile.getAbsolutePath());
+ }
+ device.executeShellCommand("rm " + pushedFile.getAbsolutePath(), shellOutput);
+ }
+
+ if (exitStatus != 0) {
+ errRedirectStream.println("Execution failed on device '" + device.getName() + "'");
+ break;
+ }
+
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (SyncException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+
+ return exitStatus;
+ }
+
+ @Nonnull
+ protected abstract List<String> buildCommandLine(@Nonnull String[] options,
+ @Nonnull String[] mainClasses, @Nonnull File... classpathFiles);
+
+ private boolean isAdbInitialized(@Nonnull AndroidDebugBridge adb) {
+ return adb.isConnected() && adb.hasInitialDeviceList();
+ }
+
+ private void ensureAdbRoot(@Nonnull IDevice device) throws RuntimeRunnerException {
+ ShellOutputToStringReceiver outputToString = new ShellOutputToStringReceiver();
+ try {
+ device.executeShellCommand("id", outputToString);
+
+ if (!outputToString.getOutput().contains("uid=0(root)")) {
+ ExecuteFile ef;
+
+ ef = new ExecuteFile("adb -s " + device.getSerialNumber() + " root");
+ ef.setOut(System.out);
+ ef.setErr(System.err);
+ ef.setVerbose(isVerbose);
+ ef.run();
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ } catch (TimeoutException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (AdbCommandRejectedException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (ShellCommandUnresponsiveException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (IOException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (ExecFileException e) {
+ throw new RuntimeRunnerException("Error while executing 'adb root'", e);
+ }
+ }
+
+ @Nonnull
+ protected abstract String getRuntimeName();
+
+ private void checkDeviceRuntime(@Nonnull IDevice device) throws RuntimeRunnerException {
+ ShellOutputToStringReceiver outputToString = new ShellOutputToStringReceiver();
+ try {
+ device.executeShellCommand("dalvikvm -showversion", outputToString);
+ if (!outputToString.getOutput().contains(getRuntimeName())) {
+ throw new TestConfigurationException(
+ "The plugged device does not run the required runtime: '" + getRuntimeName() + "'");
+ }
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/HostRunner.java b/jack-tests/src/com/android/jack/test/runner/HostRunner.java
new file mode 100644
index 0000000..a4bca8a
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/HostRunner.java
@@ -0,0 +1,52 @@
+/*
+ * 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.test.runner;
+
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.File;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This runner is used to execute tests on host.
+ */
+public abstract class HostRunner extends AbstractRuntimeRunner {
+
+ public HostRunner(@Nonnull File rtEnvironmentRootDir) {
+ super(rtEnvironmentRootDir);
+ }
+
+ protected int runOnHost(@Nonnull List<String> args, @Nonnull String rtEnvRootDirVarName)
+ throws RuntimeRunnerException {
+
+ ExecuteFile exec = new ExecuteFile(args.toArray(new String[args.size()]));
+ exec.addEnvVar(rtEnvRootDirVarName, rtEnvironmentRootDir.getAbsolutePath());
+ exec.setOut(outRedirectStream);
+ exec.setErr(errRedirectStream);
+ exec.setVerbose(isVerbose);
+
+ try {
+ int exitStatus = exec.run();
+ return exitStatus;
+ } catch (ExecFileException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/RuntimeRunner.java b/jack-tests/src/com/android/jack/test/runner/RuntimeRunner.java
new file mode 100644
index 0000000..9d8ee5f
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/RuntimeRunner.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test.runner;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Common class for tools used to run JUnit tests or main classes.
+ */
+public abstract class RuntimeRunner {
+
+ @Nonnull
+ protected File rtEnvironmentRootDir;
+
+ protected boolean isVerbose = false;
+
+ protected RuntimeRunner(@Nonnull File rtEnvironmentRootDir) {
+ this.rtEnvironmentRootDir = rtEnvironmentRootDir;
+ }
+
+ public abstract int run(@Nonnull String[] options, @Nonnull String[] className,
+ @Nonnull File... classpathFiles) throws RuntimeRunnerException;
+
+ @Nonnull
+ public RuntimeRunner setVerbose(boolean isVerbose) {
+ this.isVerbose = isVerbose;
+ return this;
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerException.java b/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerException.java
new file mode 100644
index 0000000..a353156
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.test.runner;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This exception is thrown when something went wrong with the execution
+ * of the {@link RuntimeRunner}.
+ */
+public class RuntimeRunnerException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public RuntimeRunnerException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public RuntimeRunnerException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+
+ public RuntimeRunnerException(@Nonnull String message) {
+ super(message);
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerFactory.java b/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerFactory.java
new file mode 100644
index 0000000..2d83625
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/RuntimeRunnerFactory.java
@@ -0,0 +1,87 @@
+/*
+ * 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.test.runner;
+
+import com.android.jack.test.runner.DalvikRunner.DalvikMode;
+import com.android.jack.test.toolchain.AbstractTestTools;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A factory to build {@link RuntimeRunner}s.
+ */
+public class RuntimeRunnerFactory {
+
+ private static final char SEPARATOR = '-';
+
+ /**
+ * Runtime names are composed as follows:
+ * <runtime environment name>-<variant>-<kind>
+ * Where runtime environment name is one of Dalvik, ART, ..., variant defines a variant
+ * for the selected environment (e.g. jit or fast for dalvik) and finally target is either
+ * host or device.
+ */
+ @Nonnull
+ public static RuntimeRunner create(@Nonnull String rtName) throws RuntimeRunnerException {
+
+ int firstIndex = rtName.indexOf(SEPARATOR);
+ int lastIndex = rtName.lastIndexOf(SEPARATOR);
+
+ String rtEnvName = null;
+ String variant = null;
+ String runnerKind = null;
+
+ if (firstIndex > -1) {
+ rtEnvName = rtName.substring(0, firstIndex);
+ runnerKind = rtName.substring(lastIndex + 1);
+ }
+ if (firstIndex < lastIndex) {
+ variant =
+ rtName.substring(firstIndex + 1, lastIndex);
+ }
+
+ RuntimeRunner result;
+ if ("dalvik".equals(rtEnvName)) {
+ if ("device".equals(runnerKind)) {
+ result = new DalvikRunnerDevice();
+ } else if ("host".equals(runnerKind)) {
+ result = new DalvikRunnerHost(AbstractTestTools.getRuntimeEnvironmentRootDir(rtName));
+ } else {
+ throw new RuntimeRunnerException("Unkown target for Dalvik: '" + rtName + "'");
+ }
+ if ("jit".equals(variant)) {
+ ((DalvikRunner) result).setMode(DalvikMode.JIT);
+ } else if ("fast".equals(variant)) {
+ ((DalvikRunner) result).setMode(DalvikMode.FAST);
+ } else if (variant != null) {
+ throw new RuntimeRunnerException("Unkown variant for Dalvik: '" + rtName + "'");
+ }
+ } else if ("art".equals(rtEnvName)) {
+ if ("host".equals(runnerKind)) {
+ result = new ArtRunnerHost(AbstractTestTools.getRuntimeEnvironmentRootDir(rtName));
+ } else if ("device".equals(runnerKind)) {
+ result = new ArtRunnerDevice();
+ } else {
+ throw new RuntimeRunnerException("Unkown target for ART: '" + rtName + "'");
+ }
+ } else {
+ throw new RuntimeRunnerException("Unkown runtime environment for ART: '" + rtName + "'");
+ }
+
+ return result;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/runtime/RuntimeTest.java b/jack-tests/src/com/android/jack/test/runtime/RuntimeTest.java
new file mode 100644
index 0000000..3fdb14a
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runtime/RuntimeTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test.runtime;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * {@code RuntimeTest}s must extends this class if they are to be passed as
+ * regression tests.
+ */
+public abstract class RuntimeTest {
+
+ @Nonnull
+ protected List<RuntimeTestInfo> rtTestInfos = new ArrayList<RuntimeTestInfo>();
+
+ protected RuntimeTest() {
+ fillRtTestInfos();
+ }
+
+ @Nonnull
+ public final List<RuntimeTestInfo> getRuntimeTestInfos() {
+ return rtTestInfos;
+ }
+
+ protected abstract void fillRtTestInfos();
+} \ No newline at end of file
diff --git a/jack-tests/src/com/android/jack/test/runtime/RuntimeTestInfo.java b/jack-tests/src/com/android/jack/test/runtime/RuntimeTestInfo.java
new file mode 100644
index 0000000..f5ec727
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runtime/RuntimeTestInfo.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.runtime;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class hold the information needed by the runtime tests framework to execute
+ * a test.
+ */
+public class RuntimeTestInfo {
+
+ @Nonnull
+ public File directory;
+ @Nonnull
+ public String jUnit;
+
+ public RuntimeTestInfo(@Nonnull File directory, @Nonnull String jUnit) {
+ this.directory = directory;
+ this.jUnit = jUnit;
+ }
+
+} \ No newline at end of file
diff --git a/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java
new file mode 100644
index 0000000..ed0f6b0
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java
@@ -0,0 +1,566 @@
+/*
+ * 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.test.toolchain;
+
+import com.google.common.io.Files;
+
+import com.android.jack.Sourcelist;
+import com.android.jack.test.runner.RuntimeRunner;
+import com.android.jack.test.runner.RuntimeRunnerException;
+import com.android.jack.test.runner.RuntimeRunnerFactory;
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+import com.android.jack.util.NamingTools;
+
+import org.junit.Assume;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Utility class that act also as a Factory for toolchains. It holds the global
+ * configuration for tests.
+ */
+public abstract class AbstractTestTools {
+
+ @Nonnull
+ public static final String JUNIT_RUNNER_NAME = "org.junit.runner.JUnitCore";
+ @Nonnull
+ public static final String TESTS_CONFIGURATION_FILE_VARIABLE = "TESTS_CONFIGURATION_FILE";
+
+ @Nonnull
+ private static HashMap<String, ToolchainBuilder> toolchainBuilders;
+
+ @Nonnull
+ private static final String JACK_TESTS_FOLDER =
+ "toolchain" + File.separator + "jack" + File.separator + "jack-tests";
+
+ @Nonnull
+ private static final Properties testsProperties;
+
+ @Nonnull
+ private static final String PROPERTY_VALUE_SEPARATOR = ",";
+ @Nonnull
+ private static final String TOOLCHAIN_REFERENCE_KEY = "toolchain.reference";
+ @Nonnull
+ private static final String TOOLCHAIN_CANDIDATE_KEY = "toolchain.candidate";
+ @Nonnull
+ private static final String RUNTIME_LIST_KEY = "runtime.list";
+ @Nonnull
+ private static final String RUNTIME_LOCATION_PREFIX = "runtime.location.";
+ @Nonnull
+ private static final String TOOL_PREFIX = "tool.";
+ @Nonnull
+ private static final String TOOLCHAIN_PREBUILT_PREFIX = "toolchain.prebuilt.";
+
+ @Nonnull
+ private static final List<RuntimeRunner> runtimes = new ArrayList<RuntimeRunner>();
+
+ @Nonnull
+ private static final Map<String, File> runtimeEnvironmentLocations = new HashMap<String, File>();
+
+
+ private interface ToolchainBuilder {
+ IToolchain build();
+ }
+
+ private static class LegacyToolchainBuilder implements ToolchainBuilder {
+
+ @Override
+ @Nonnull
+ public LegacyToolchain build() {
+ return new LegacyToolchain(getPrebuilt("legacy-java-compiler"), getPrebuilt("jarjar"),
+ getPrebuilt("proguard"));
+ }
+ }
+
+ private static class JackCliToolchainBuilder implements ToolchainBuilder {
+
+ @Override
+ @Nonnull
+ public JackCliToolchain build() {
+ return new JackCliToolchain(getPrebuilt("jack"));
+ }
+ }
+
+ private static class JackApiToolchainBuilder implements ToolchainBuilder {
+
+ @Override
+ @Nonnull
+ public JackApiToolchain build() {
+ return new JackApiToolchain();
+ }
+ }
+
+ private static class LegacyJillToolchainBuilder implements ToolchainBuilder {
+
+ @Override
+ public IToolchain build() {
+ return new LegacyJillToolchain(getPrebuilt("jill"), getPrebuilt("jack"),
+ getPrebuilt("jarjar"), getPrebuilt("proguard"));
+ }
+ }
+
+ private static File getPrebuilt(@Nonnull String prebuiltName) {
+ String prebuiltPath = getProperty(TOOLCHAIN_PREBUILT_PREFIX + prebuiltName);
+
+ if (prebuiltPath == null) {
+ throw new TestConfigurationException(
+ "Cannot find path for prebuilt 'prebuiltName' in test.properties");
+ }
+
+ File result = new File(prebuiltPath);
+ if (!result.isAbsolute()) {
+ result = new File(getJackRootDir(), prebuiltPath);
+ }
+
+ if (!result.exists()) {
+ throw new TestConfigurationException(
+ "Can not find '" + prebuiltName + "' prebuilt at '" + result.getPath() + "'");
+ }
+ return result;
+ }
+
+ static {
+ toolchainBuilders = new HashMap<String, ToolchainBuilder>();
+ toolchainBuilders.put("jack-cli" , new JackCliToolchainBuilder());
+ toolchainBuilders.put("jack-api" , new JackApiToolchainBuilder());
+ toolchainBuilders.put("legacy" , new LegacyToolchainBuilder());
+ toolchainBuilders.put("jill-legacy", new LegacyJillToolchainBuilder());
+
+ testsProperties = new Properties();
+ String filePath = System.getenv(TESTS_CONFIGURATION_FILE_VARIABLE);
+ File propertyFile;
+ if (filePath != null) {
+ propertyFile = new File(filePath);
+ if (!propertyFile.isAbsolute()) {
+ propertyFile = new File(System.getProperty("user.dir"), filePath);
+ }
+ } else {
+ filePath = JACK_TESTS_FOLDER + File.separatorChar + "tests.properties";
+ propertyFile =
+ new File(getJackRootDir(), filePath);
+ }
+
+ if (!propertyFile.exists()) {
+ throw new TestConfigurationException("Configuration file not found: '" + filePath + "'");
+ }
+
+ try {
+ testsProperties.load(new FileInputStream(propertyFile));
+ runtimes.addAll(parseRuntimeList(testsProperties.getProperty(RUNTIME_LIST_KEY)));
+ } catch (FileNotFoundException e) {
+ throw new TestConfigurationException(e);
+ } catch (IOException e) {
+ throw new TestConfigurationException(e);
+ } catch (SecurityException e) {
+ throw new TestConfigurationException(e);
+ } catch (IllegalArgumentException e) {
+ throw new TestConfigurationException(e);
+ } catch (RuntimeRunnerException e) {
+ throw new TestConfigurationException(e);
+ }
+ }
+
+ @Nonnull
+ public static final File getJackRootDir() {
+ String pwdPath = System.getProperty("user.dir");
+ String[] splitPath = pwdPath.split(JACK_TESTS_FOLDER);
+ if (splitPath[0].equals(pwdPath)) {
+ assert splitPath.length == 1;
+ throw new AssertionError("Unable to compute tests root directory");
+ }
+ return new File(splitPath[0]);
+ }
+
+ @Nonnull
+ private static final File getTestsRootDir() {
+ return new File(getJackRootDir(), JACK_TESTS_FOLDER + File.separator + "tests");
+ }
+
+ @Nonnull
+ public static final File getTestRootDir(@Nonnull String packageName) {
+ return new File(getTestsRootDir(), packageName.replace(".", File.separator));
+ }
+
+ /**
+ * Return the {@link IToolchain} specified in the tests configuration file if it matches the
+ * requirements expressed in paramters. Otherwise, test is ignored.
+ *
+ * @param classes Optional list of types. The first one is used to check that the candidate type
+ * is of this type. Otherwise JUnit test will be ignored. If more types are provided, they
+ * serve to narrow the expected type set, and are used for exclusion, i.e. if returned
+ * type is of one of these types, test is ignored.
+ * @return The candidate toolchain that fulfills the requirements.
+ */
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public static final <T extends IToolchain> T getCandidateToolchain(
+ @Nonnull Class<? extends IToolchain>... classes) {
+ IToolchain result = createToolchain("candidate.toolchain");
+ if (classes.length > 0) {
+ Assume.assumeTrue(classes[0].isAssignableFrom(result.getClass()));
+ for (int i = 1; i < classes.length; i++) {
+ Assume.assumeTrue(!classes[i].isAssignableFrom(result.getClass()));
+ }
+ }
+ return (T) result;
+ }
+
+ @Nonnull
+ public static final IToolchain getReferenceToolchain() {
+ return createToolchain("reference.toolchain");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public static final <T extends IToolchain> T getReferenceToolchain(
+ @Nonnull Class<?> expectedClass) {
+ IToolchain result = getReferenceToolchain();
+ Assume.assumeTrue(expectedClass.isAssignableFrom(result.getClass()));
+ return (T) result;
+ }
+
+ @Nonnull
+ private static IToolchain createToolchain(@Nonnull String propertyName) {
+ return getToolchainBuilder(getProperty(propertyName)).build();
+ }
+
+ @Nonnull
+ private static String getProperty(@Nonnull String key) {
+ String value = testsProperties.getProperty(key);
+ if (value == null) {
+ throw new TestConfigurationException("Undefined property : '" + key + "'");
+ }
+ return value;
+ }
+
+ @Nonnull
+ private static ToolchainBuilder getToolchainBuilder(@Nonnull String toolchainName) {
+ ToolchainBuilder toolchainBuilder = toolchainBuilders.get(toolchainName);
+ if (toolchainBuilder == null) {
+ throw new TestConfigurationException("Unknown toolchain: '" + toolchainName + "'");
+ }
+ return toolchainBuilder;
+ }
+
+ @Nonnull
+ public static File createTempFile(@Nonnull String prefix, @Nonnull String suffix)
+ throws IOException {
+ File tmp = File.createTempFile(prefix, suffix);
+ tmp.deleteOnExit();
+ return tmp;
+ }
+
+ @Nonnull
+ public static File createTempDir() throws IOException {
+ try {
+ final File tmpDir = Files.createTempDir();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ try {
+ deleteTempDir(tmpDir);
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ });
+ return tmpDir;
+ } catch (IllegalStateException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Nonnull
+ public static File createDir(@Nonnull File directory, @Nonnull String name) throws IOException {
+ if (!directory.exists() || !directory.isDirectory()) {
+ throw new AssertionError();
+ }
+
+ File result = new File(directory, name);
+ if (!result.mkdir()) {
+ throw new IOException("Failed to create dir " + result.getAbsolutePath());
+ }
+ return result;
+ }
+
+ public static void deleteTempDir(@CheckForNull File tmp) throws IOException {
+ if (tmp == null) {
+ return;
+ }
+
+ if (tmp.isDirectory()) {
+ for (File sub : tmp.listFiles()) {
+ deleteTempDir(sub);
+ }
+ }
+ if (!tmp.delete()) {
+ throw new IOException("Failed to delete file " + tmp.getAbsolutePath());
+ }
+ }
+
+ @Nonnull
+ public static String getClasspathAsString(@Nonnull File[] files) {
+ if (files.length == 0) {
+ return "";
+ }
+ StringBuilder classpathStr = new StringBuilder();
+ for (int i = 0; i < files.length; i++) {
+ classpathStr.append(files[i].getAbsolutePath());
+ if (i != files.length - 1) {
+ classpathStr.append(File.pathSeparatorChar);
+ }
+ }
+ return classpathStr.toString();
+ }
+
+ @Nonnull
+ public static Properties getGlobalProperties() {
+ return testsProperties;
+ }
+
+ @Nonnull
+ public static String getClasspathsAsString(
+ @Nonnull File[] bootClasspath, @Nonnull File[] classpath) {
+ if (bootClasspath.length == 0) {
+ return getClasspathAsString(classpath);
+ } else if (classpath.length == 0) {
+ return getClasspathAsString(bootClasspath);
+ } else {
+ return concatClasspathStrings(
+ getClasspathAsString(bootClasspath), getClasspathAsString(classpath));
+ }
+ }
+
+ @Nonnull
+ private static String concatClasspathStrings(
+ @Nonnull String bootclasspath, @Nonnull String classpath) {
+ if (bootclasspath.isEmpty()) {
+ return classpath;
+ } else if (classpath.isEmpty()) {
+ return bootclasspath;
+ } else {
+ StringBuilder classpathStr = new StringBuilder(bootclasspath);
+ classpathStr.append(File.pathSeparatorChar);
+ classpathStr.append(classpath);
+ return classpathStr.toString();
+ }
+ }
+
+ @Nonnull
+ public static File createJavaFile(@Nonnull File folder, @Nonnull String packageName,
+ @Nonnull String fileName, @Nonnull String fileContent) throws IOException {
+ File packageFolder = new File(folder, packageName.replace('.', File.separatorChar));
+ if (!packageFolder.exists() && !packageFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + packageFolder.getAbsolutePath());
+ }
+ File javaFile = new File(packageFolder, fileName);
+ if (javaFile.exists() && !javaFile.delete()) {
+ throw new IOException("Failed to delete file " + javaFile.getAbsolutePath());
+ }
+ if (!javaFile.createNewFile()) {
+ throw new IOException("Failed to create file " + javaFile.getAbsolutePath());
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(javaFile);
+ fos.write(fileContent.getBytes());
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ return javaFile;
+ }
+
+ public static void deleteJavaFile(@Nonnull File folder, @Nonnull String packageName,
+ @Nonnull String fileName) throws IOException {
+ File packageFolder = new File(folder, NamingTools.getBinaryName(packageName));
+ File javaFile = new File(packageFolder, fileName);
+ deleteFile(javaFile);
+ }
+
+ public static void deleteFile(@Nonnull File file) throws IOException {
+ if (!file.delete()) {
+ throw new IOException("Failed to delete file " + file.getAbsolutePath());
+ }
+ }
+
+ @Nonnull
+ public static File getDir(@Nonnull File file) {
+ if (file.isDirectory()) {
+ return file;
+ } else {
+ return file.getParentFile();
+ }
+ }
+
+ @Nonnull
+ public static List<File> getFiles(@Nonnull File folder, @Nonnull String extension) {
+ assert folder.isDirectory();
+ List<File> jackFiles = new ArrayList<File>();
+ fillWithFiles(folder, jackFiles, extension);
+ return jackFiles;
+ }
+
+ private static void fillWithFiles(@Nonnull File file, @Nonnull List<File> jackFiles,
+ @Nonnull String extension) {
+ if (file.isDirectory()) {
+ for (File subFile : file.listFiles()) {
+ fillWithFiles(subFile, jackFiles, extension);
+ }
+ } else if (extension == null || extension.equals("*") || file.getName().endsWith(extension)) {
+ jackFiles.add(file);
+ }
+ }
+
+ public static void addFile(@Nonnull List<String> args,
+ boolean mustExist, @Nonnull File... filesOrSourceLists) {
+ for (File file : filesOrSourceLists) {
+ addFile(file, args, mustExist);
+ }
+ }
+
+ private static void addFile(@Nonnull File fileOrSourceList, @Nonnull List<String> args,
+ boolean mustExist) {
+ if (fileOrSourceList instanceof Sourcelist) {
+ args.add("@" + fileOrSourceList.getAbsolutePath());
+ } else {
+ List<File> sourceFiles = new ArrayList<File>();
+ try {
+ getJavaFiles(fileOrSourceList, sourceFiles, mustExist);
+ } catch (IOException e) {
+ }
+ for (File sourceFile : sourceFiles) {
+ args.add(sourceFile.getAbsolutePath());
+ }
+ }
+ }
+
+ public static void getJavaFiles(@Nonnull File fileObject, @Nonnull List<File> filePaths,
+ boolean mustExist) throws IOException {
+ if (fileObject.isDirectory()) {
+ File allFiles[] = fileObject.listFiles();
+ for (File aFile : allFiles) {
+ getJavaFiles(aFile, filePaths, mustExist);
+ }
+ } else if (fileObject.getName().endsWith(".java") && (!mustExist || fileObject.isFile())) {
+ filePaths.add(fileObject.getCanonicalFile());
+ }
+ }
+
+ public static void unzip(@Nonnull File jarfile, @Nonnull File outputFolder) {
+ String[] args = new String[] {"unzip", "-qo", jarfile.getAbsolutePath(), "-d",
+ outputFolder.getAbsolutePath()};
+
+ ExecuteFile execFile = new ExecuteFile(args);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Unzip exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running unzip", e);
+ }
+ }
+
+ public static void createjar(@Nonnull File jarfile, @Nonnull File inputFiles) {
+ String[] args = new String[] {"jar",
+ "cf",
+ jarfile.getAbsolutePath(),
+ "-C",
+ inputFiles.getAbsolutePath(),
+ "."};
+
+ ExecuteFile execFile = new ExecuteFile(args);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Jar exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running jar command", e);
+ }
+ }
+
+ @Nonnull
+ protected static List<String> buildEcjArgs() {
+ List<String> ecjArgs = new ArrayList<String>();
+ ecjArgs.add("-nowarn");
+
+ return ecjArgs;
+ }
+
+ @Nonnull
+ public static List<RuntimeRunner> listRuntimeTestRunners(@CheckForNull Properties properties)
+ throws SecurityException, IllegalArgumentException, RuntimeRunnerException {
+
+ if (properties != null) {
+ String rtAsString = properties.getProperty(RUNTIME_LIST_KEY);
+ if (rtAsString != null) {
+ return parseRuntimeList(rtAsString);
+ }
+ }
+ return runtimes;
+ }
+
+ @Nonnull
+ private static List<RuntimeRunner> parseRuntimeList(@CheckForNull String runtimeList)
+ throws SecurityException, IllegalArgumentException, RuntimeRunnerException {
+ List<RuntimeRunner> result = new ArrayList<RuntimeRunner>(0);
+ if (runtimeList != null) {
+ String[] rtList = runtimeList.split(PROPERTY_VALUE_SEPARATOR);
+ for (String rtName : rtList) {
+ result.add(RuntimeRunnerFactory.create(rtName));
+ }
+ }
+ return result;
+ }
+
+ @Nonnull
+ public static File getRuntimeEnvironmentRootDir(@Nonnull String rtName) {
+ String rtLocationPath = testsProperties.getProperty(RUNTIME_LOCATION_PREFIX + rtName);
+
+ if (rtLocationPath == null) {
+ throw new TestConfigurationException(
+ "Location for runtime '" + rtName + "' is not specified");
+ }
+ File rtLocation = new File(rtLocationPath);
+ if (!rtLocation.exists()) {
+ throw new TestConfigurationException(
+ "Location for runtime " + rtName + " does not exist: '" + rtLocationPath + "'");
+ }
+ if (!rtLocation.isDirectory()) {
+ throw new TestConfigurationException(
+ "Location for runtime " + rtName + " is not a directory: '" + rtLocationPath + "'");
+ }
+
+ return rtLocation;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/AndroidToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/AndroidToolchain.java
new file mode 100644
index 0000000..12a1215
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/AndroidToolchain.java
@@ -0,0 +1,49 @@
+/*
+ * 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.test.toolchain;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A {@link Toolchain} to produce libs and executable for the Android platform.
+ */
+public abstract class AndroidToolchain extends Toolchain {
+
+ @Override
+ @Nonnull
+ public final String getExeExtension() {
+ return ".dex";
+ }
+
+ @Override
+ @Nonnull
+ public final String getLibraryExtension() {
+ return ".jar";
+ }
+
+ @Nonnull
+ public final String getBinaryFileName() {
+ return "classes.dex";
+ }
+
+ @Nonnull
+ public abstract AndroidToolchain disableDxOptimizations();
+
+ @Nonnull
+ public abstract AndroidToolchain enableDxOptimizations();
+
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/DummyToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/DummyToolchain.java
new file mode 100644
index 0000000..9e13068
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/DummyToolchain.java
@@ -0,0 +1,76 @@
+/*
+ * 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.test.toolchain;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {link AndroidToolchain} actually does nothing. It can be used in a comparison
+ * test to simulate one of the toolchain whereas the data to compare is already
+ * available, such as an expected result in a text file for instance.
+ */
+public class DummyToolchain extends AndroidToolchain {
+
+ @Nonnull
+ private final File[] dummyBootclasspath = new File[0];
+
+ public DummyToolchain() {}
+
+ @Override
+ @Nonnull
+ public void srcToExe(@Nonnull String classpath, @Nonnull File out,
+ @Nonnull File... sources) throws Exception {
+ }
+
+ @Override
+ @Nonnull
+ public void srcToLib(@Nonnull String classpath, @Nonnull File out,
+ boolean zipFiles, @Nonnull File... sources) throws Exception {
+ }
+
+ @Override
+ @Nonnull
+ public void libToDex(@Nonnull File in, @Nonnull File out) throws Exception {
+ }
+
+ @Override
+ @Nonnull
+ public void libToLib(@Nonnull File in, @Nonnull File out) throws Exception {
+ }
+
+ @Override
+ @Nonnull
+ public File[] getDefaultBootClasspath() {
+ return dummyBootclasspath;
+ }
+
+ @Override
+ @Nonnull
+ public DummyToolchain disableDxOptimizations() {
+ // Do nothing
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public DummyToolchain enableDxOptimizations() {
+ // Do nothing
+ return this;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java
new file mode 100644
index 0000000..8c35d1d
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java
@@ -0,0 +1,81 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.jack.test.toolchain.Toolchain.SourceLevel;
+
+import java.io.File;
+import java.io.OutputStream;
+
+import javax.annotation.Nonnull;
+import javax.annotation.processing.Processor;
+
+
+/**
+ * Abstraction of a toolchain which takes source files and produce libraries and executables.
+ */
+public interface IToolchain {
+
+ @Nonnull
+ void srcToExe(@Nonnull String classpath, @Nonnull File out, @Nonnull File... sources)
+ throws Exception;
+
+ @Nonnull
+ void srcToLib(@Nonnull String classpath, @Nonnull File out, boolean zipFiles,
+ @Nonnull File... sources) throws Exception;
+
+ @Nonnull
+ void libToDex(@Nonnull File in, @Nonnull File out) throws Exception;
+
+ @Nonnull
+ void libToLib(@Nonnull File in, @Nonnull File out) throws Exception;
+
+ @Nonnull
+ IToolchain addStaticLibs(@Nonnull File... staticLibs);
+
+ @Nonnull
+ File[] getDefaultBootClasspath();
+
+ @Nonnull
+ String getExeExtension();
+
+ @Nonnull
+ String getLibraryExtension();
+
+ @Nonnull
+ IToolchain setWithDebugInfos(boolean withDebugInfos);
+
+ @Nonnull
+ IToolchain setAnnotationProcessorClass(
+ @Nonnull Class<? extends Processor> annotationProcessorClass);
+
+ @Nonnull
+ IToolchain setSourceLevel(@Nonnull SourceLevel sourceLevel);
+
+ @Nonnull
+ IToolchain addProguardFlags(@Nonnull File... proguardFlags);
+
+ @Nonnull
+ IToolchain setJarjarRules(@Nonnull File jarjarRules);
+
+ @Nonnull
+ IToolchain setOutputStream(@Nonnull OutputStream outputStream);
+
+ @Nonnull
+ IToolchain setErrorStream(@Nonnull OutputStream errorStream);
+
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchain.java
new file mode 100644
index 0000000..7466b93
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchain.java
@@ -0,0 +1,230 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.jack.Jack;
+import com.android.jack.Options;
+import com.android.jack.experimental.incremental.JackIncremental;
+import com.android.jack.shrob.spec.Flags;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This class implements a {@link JackBasedToolchain} by calling Jack via API.
+ */
+public class JackApiToolchain extends JackBasedToolchain {
+
+ @Nonnull
+ private Options jackOptions = new Options();
+
+ JackApiToolchain() {}
+
+ @Override
+ @Nonnull
+ public JackApiToolchain disableDxOptimizations() {
+ jackOptions.disableDxOptimizations();
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public JackApiToolchain enableDxOptimizations() {
+ jackOptions.enableDxOptimizations();
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public void srcToExe(@Nonnull String classpath, @Nonnull File out, @Nonnull File... sources)
+ throws Exception {
+
+ try {
+ System.setOut(outRedirectStream);
+ System.setErr(errRedirectStream);
+
+ addProperties(properties, jackOptions);
+
+ if (jackOptions.getFlags() != null) {
+ jackOptions.applyShrobFlags();
+ }
+
+ jackOptions.setEcjArguments(AbstractTestTools.buildEcjArgs());
+
+ if (annotationProcessorClass != null) {
+ jackOptions.getEcjArguments().add("-processor");
+ jackOptions.getEcjArguments().add(annotationProcessorClass.getName());
+ }
+
+ if (annotationProcessorOutDir != null) {
+ jackOptions.getEcjArguments().add("-d");
+ jackOptions.getEcjArguments().add(annotationProcessorOutDir.getAbsolutePath());
+ }
+
+ for (String ecjArg : extraEcjArgs) {
+ jackOptions.getEcjArguments().add(ecjArg);
+ }
+
+ AbstractTestTools.addFile(jackOptions.getEcjArguments(),
+ /* mustExist = */false, sources);
+ jackOptions.setClasspath(classpath);
+
+ // !zip
+ jackOptions.setOutputDir(out);
+
+ jackOptions.setJayceImports(staticLibs);
+
+ jackOptions.setJarjarRulesFile(jarjarRules);
+ List<File> proguardFlagsFiles = new ArrayList<File>();
+
+ for (File flagFile : proguardFlagsFiles) {
+ proguardFlagsFiles.add(flagFile);
+ }
+
+ if (proguardFlagsFiles.size() > 0) {
+ jackOptions.setProguardFlagsFile(proguardFlagsFiles);
+ }
+
+ jackOptions.addProperty(Options.EMIT_LOCAL_DEBUG_INFO.getName(),
+ Boolean.toString(withDebugInfos));
+
+ if (jackOptions.getIncrementalFolder() != null) {
+ JackIncremental.run(jackOptions);
+ } else {
+ Jack.run(jackOptions);
+ }
+
+ } finally {
+ System.setOut(stdOut);
+ System.setErr(stdErr);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void srcToLib(@Nonnull String classpath, @Nonnull File out, boolean zipFiles,
+ @Nonnull File... sources) throws Exception {
+
+ try {
+ Options options = jackOptions;
+
+ addProperties(properties, options);
+
+ options.setClasspath(classpath);
+
+ if (zipFiles) {
+ options.setJayceOutputZip(out);
+ } else {
+ options.setJayceOutputDir(out);
+ }
+
+ options.setEcjArguments(AbstractTestTools.buildEcjArgs());
+
+ if (annotationProcessorClass != null) {
+ options.getEcjArguments().add("-processor");
+ options.getEcjArguments().add(annotationProcessorClass.getName());
+ }
+
+ if (annotationProcessorOutDir != null) {
+ options.getEcjArguments().add("-d");
+ options.getEcjArguments().add(annotationProcessorOutDir.getAbsolutePath());
+ }
+
+ for (String ecjArg : extraEcjArgs) {
+ options.getEcjArguments().add(ecjArg);
+ }
+
+ AbstractTestTools.addFile(options.getEcjArguments(),
+ /* mustExist = */false, sources);
+
+ options.addProperty(Options.EMIT_LOCAL_DEBUG_INFO.getName(),
+ Boolean.toString(withDebugInfos));
+
+ System.setOut(outRedirectStream);
+ System.setErr(errRedirectStream);
+
+ if (options.getIncrementalFolder() != null) {
+ JackIncremental.run(options);
+ } else {
+ Jack.run(options);
+ }
+
+ } finally {
+ System.setOut(stdOut);
+ System.setErr(stdErr);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToDex(@Nonnull File in, @Nonnull File out) throws Exception {
+ System.setOut(outRedirectStream);
+ System.setErr(errRedirectStream);
+
+ try {
+ Options options = jackOptions;
+ addProperties(properties, options);
+
+ options.getJayceImport().add(in);
+ options.getJayceImport().addAll(staticLibs);
+
+ // !zip
+ options.setOutputDir(out);
+
+ if (options.getIncrementalFolder() != null) {
+ JackIncremental.run(options);
+ } else {
+ Jack.run(options);
+ }
+
+ } finally {
+ System.setOut(stdOut);
+ System.setErr(stdErr);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToLib(@Nonnull File in, @Nonnull File out) throws Exception {
+ throw new AssertionError("Not Yet Implemented");
+ }
+
+ @Nonnull
+ public JackApiToolchain setShrobFlags(@Nonnull Flags shrobFlags) {
+ jackOptions.setFlags(shrobFlags);
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public JackApiToolchain setIncrementalFolder(@Nonnull File incrementalFolder) {
+ jackOptions.setIncrementalFolder(incrementalFolder);
+ return this;
+ }
+
+ private static final void addProperties(@Nonnull Map<String, String> properties,
+ @Nonnull Options jackOptions) {
+ for (Entry<String, String> entry : properties.entrySet()) {
+ jackOptions.addProperty(entry.getKey(), entry.getValue());
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java
new file mode 100644
index 0000000..729eb89
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java
@@ -0,0 +1,88 @@
+/*
+ * 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.test.toolchain;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Defines an {@link AndroidToolchain} built on Jack.
+ */
+public abstract class JackBasedToolchain extends AndroidToolchain {
+
+ @Nonnull
+ protected final Map<String, String> properties = new HashMap<String, String>();
+ @CheckForNull
+ protected File annotationProcessorOutDir;
+ @Nonnull
+ protected List<String> extraEcjArgs = new ArrayList<String>();
+
+ @Nonnull
+ public final JackBasedToolchain addProperty(@Nonnull String propertyName,
+ @Nonnull String propertyValue) {
+ properties.put(propertyName, propertyValue);
+ return this;
+ }
+
+ @Nonnull
+ public final JackBasedToolchain addEcjArgs(@Nonnull String arg) {
+ extraEcjArgs.add(arg);
+ return this;
+ }
+
+ @Nonnull
+ public final JackBasedToolchain setAnnotationProcessorOutDir(
+ @Nonnull File annotationProcessorOutDir) {
+ this.annotationProcessorOutDir = annotationProcessorOutDir;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public JackBasedToolchain setSourceLevel(@Nonnull SourceLevel sourceLevel) {
+ super.setSourceLevel(sourceLevel);
+ switch (sourceLevel) {
+ case JAVA_6:
+ addProperty("jack.java.source.version", "1.6");
+ break;
+ case JAVA_7:
+ addProperty("jack.java.source.version", "1.7");
+ break;
+ default:
+ throw new AssertionError("Unkown level: '" + sourceLevel.toString() + "'");
+ }
+ return this;
+ }
+
+ @Nonnull
+ public abstract JackBasedToolchain setIncrementalFolder(@Nonnull File incrementalFolder);
+
+ @Override
+ @Nonnull
+ public File[] getDefaultBootClasspath() {
+ return new File[] {new File(AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/libs/core-stubs-mini.jar"), new File(
+ AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/prebuilts/junit4-hostdex.jar")};
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java
new file mode 100644
index 0000000..17f11dd
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java
@@ -0,0 +1,281 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.jack.backend.dex.rop.CodeItemBuilder;
+import com.android.jack.util.ExecuteFile;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * This class implements a {@link JackBasedToolchain} by calling Jack via command line.
+ */
+public class JackCliToolchain extends JackBasedToolchain {
+
+ @Nonnull
+ protected File jackPrebuilt;
+
+ @Nonnull
+ private List<String> extraJackArgs = new ArrayList<String>(0);
+ @CheckForNull
+ private File incrementalFolder;
+
+ JackCliToolchain(@Nonnull File prebuilt) {
+ this.jackPrebuilt = prebuilt;
+ }
+
+ @Override
+ @Nonnull
+ public void srcToExe(@Nonnull String classpath, @Nonnull File out,
+ @Nonnull File... sources) throws Exception {
+
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-cp");
+ args.add(jackPrebuilt.getAbsolutePath());
+
+ if (incrementalFolder != null) {
+ args.add(com.android.jack.experimental.incremental.Main.class.getName());
+ args.add("--incremental-folder");
+ args.add(incrementalFolder.getAbsolutePath());
+ } else {
+ args.add(com.android.jack.Main.class.getName());
+ }
+
+ if (withDebugInfos) {
+ args.add("-D");
+ args.add("jack.dex.optimize=false");
+ } else {
+ args.add("-D");
+ args.add("jack.dex.optimize=true");
+ }
+
+ addProperties(properties, args);
+
+ args.add("--classpath");
+ args.add(classpath);
+
+ args.add("-o");
+ args.add(out.getAbsolutePath());
+
+ if (jarjarRules != null) {
+ args.add("--jarjar-rules");
+ args.add(jarjarRules.getAbsolutePath());
+ }
+
+ for (File flags : proguardFlags) {
+ args.add("--proguard-flags");
+ args.add(flags.getAbsolutePath());
+ }
+
+ for (File staticLib : staticLibs) {
+ args.add("--import-jack");
+ args.add(staticLib.getAbsolutePath());
+ }
+
+ args.addAll(extraJackArgs);
+
+ args.add("--ecj");
+
+ if (withDebugInfos) {
+ args.add("-g");
+ }
+
+ if (annotationProcessorClass != null) {
+ args.add("-processor");
+ args.add(annotationProcessorClass.getName());
+ }
+ if (annotationProcessorOutDir != null) {
+ args.add("-d");
+ args.add(annotationProcessorOutDir.getAbsolutePath());
+ }
+ for (String ecjArg : extraEcjArgs) {
+ args.add(ecjArg);
+ }
+
+ AbstractTestTools.addFile(args, /* mustExist = */ false, sources);
+
+ ExecuteFile exec = new ExecuteFile(args.toArray(new String[args.size()]));
+ exec.setErr(outRedirectStream);
+ exec.setOut(errRedirectStream);
+ exec.setVerbose(true);
+
+ if (!exec.run()) {
+ throw new RuntimeException("Jack compiler exited with an error");
+ }
+
+ }
+
+ @Override
+ @Nonnull
+ public void srcToLib(@Nonnull String classpath, @Nonnull File out,
+ boolean zipFiles, @Nonnull File... sources) throws Exception {
+
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-cp");
+ args.add(jackPrebuilt.getAbsolutePath());
+
+ if (incrementalFolder != null) {
+ args.add(com.android.jack.experimental.incremental.Main.class.getName());
+ args.add("--incremental-folder");
+ args.add(incrementalFolder.getAbsolutePath());
+ } else {
+ args.add(com.android.jack.Main.class.getName());
+ }
+
+ addProperties(properties, args);
+
+ args.add("--classpath");
+ args.add(classpath);
+
+ if (zipFiles) {
+ args.add("--jack-output-zip");
+ } else {
+ args.add("--jack-output");
+ }
+ args.add(out.getAbsolutePath());
+
+ args.addAll(extraJackArgs);
+
+ args.add("--ecj");
+
+ if (withDebugInfos) {
+ args.add("-g");
+ }
+
+ if (annotationProcessorClass != null) {
+ args.add("-processor");
+ args.add(annotationProcessorClass.getName());
+ }
+ if (annotationProcessorOutDir != null) {
+ args.add("-d");
+ args.add(annotationProcessorOutDir.getAbsolutePath());
+ }
+ for (String ecjArg : extraEcjArgs) {
+ args.add(ecjArg);
+ }
+
+ AbstractTestTools.addFile(args, /* mustExist = */ false, sources);
+
+ ExecuteFile exec = new ExecuteFile(args.toArray(new String[args.size()]));
+ exec.setErr(outRedirectStream);
+ exec.setOut(errRedirectStream);
+ exec.setVerbose(true);
+
+ if (!exec.run()) {
+ throw new RuntimeException("Jack compiler exited with an error");
+ }
+
+ }
+
+ @Override
+ @Nonnull
+ public void libToDex(@Nonnull File in, @Nonnull File out) throws Exception {
+
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-cp");
+ args.add(jackPrebuilt.getAbsolutePath());
+
+ if (incrementalFolder != null) {
+ args.add(com.android.jack.experimental.incremental.Main.class.getName());
+ args.add("--incremental-folder");
+ args.add(incrementalFolder.getAbsolutePath());
+ } else {
+ args.add(com.android.jack.Main.class.getName());
+ }
+
+ if (withDebugInfos) {
+ args.add("-D");
+ args.add("jack.dex.optimize=false");
+ } else {
+ args.add("-D");
+ args.add("jack.dex.optimize=true");
+ }
+
+ addProperties(properties, args);
+
+ args.add("--import-jack");
+ args.add(in.getAbsolutePath());
+
+ for (File staticLib : staticLibs) {
+ args.add("--import-jack");
+ args.add(staticLib.getAbsolutePath());
+ }
+
+ args.add("-o");
+ args.add(out.getAbsolutePath());
+
+ ExecuteFile exec = new ExecuteFile(args.toArray(new String[args.size()]));
+ exec.setErr(outRedirectStream);
+ exec.setOut(errRedirectStream);
+ exec.setVerbose(true);
+
+ if (!exec.run()) {
+ throw new RuntimeException("Jack compiler exited with an error");
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToLib(@Nonnull File in, @Nonnull File out) throws Exception {
+ throw new AssertionError("Not Yet Implemented");
+ }
+
+ @Override
+ @Nonnull
+ public JackCliToolchain disableDxOptimizations() {
+ addProperty(CodeItemBuilder.DEX_OPTIMIZE.getName(), "false");
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public JackCliToolchain enableDxOptimizations() {
+ addProperty(CodeItemBuilder.DEX_OPTIMIZE.getName(), "true");
+ return this;
+ }
+
+ @Nonnull
+ public JackCliToolchain addJackArg(@Nonnull String arg) {
+ extraJackArgs.add(arg);
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public JackCliToolchain setIncrementalFolder(@Nonnull File incrementalFolder) {
+ this.incrementalFolder = incrementalFolder;
+ return this;
+ }
+
+ private static void addProperties(@Nonnull Map<String, String> properties,
+ @Nonnull List<String> args) {
+ for (Entry<String, String> entry : properties.entrySet()) {
+ args.add("-D");
+ args.add(entry.getKey() + "=" + entry.getValue());
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java
new file mode 100644
index 0000000..dbcc528
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java
@@ -0,0 +1,69 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link AndroidToolchain} uses Jill to convert legacy library format
+ * to dex format.
+ */
+public abstract class JillBasedToolchain extends JackCliToolchain {
+
+ @Nonnull
+ private File jillPrebuilt;
+
+ JillBasedToolchain(@Nonnull File jillPrebuilt, @Nonnull File jackPrebuilt) {
+ super(jackPrebuilt);
+ this.jillPrebuilt = jillPrebuilt;
+ }
+
+ protected void executeJill(@Nonnull File in, @Nonnull File out, boolean zipFiles) {
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-jar");
+ args.add(jillPrebuilt.getAbsolutePath());
+ if (zipFiles) {
+ args.add("--container");
+ args.add("zip");
+ }
+ args.add(in.getAbsolutePath());
+ args.add("-o");
+ args.add(out.getAbsolutePath());
+
+ ExecuteFile execFile = new ExecuteFile(args.toArray(new String[args.size()]));
+ execFile.setOut(outRedirectStream);
+ execFile.setErr(errRedirectStream);
+ execFile.setVerbose(true);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Jill exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running Jill", e);
+ }
+ }
+
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java
new file mode 100644
index 0000000..5ebf6e4
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java
@@ -0,0 +1,229 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@link JillBasedToolchain} uses legacy java compiler as a frontend.
+ */
+public class LegacyJillToolchain extends JillBasedToolchain {
+
+ @Nonnull
+ private File jarjarPrebuilt;
+ @Nonnull
+ private File proguardPrebuilt;
+
+ public LegacyJillToolchain(@Nonnull File jillPrebuilt, @Nonnull File jackPrebuilt,
+ @Nonnull File jarjarPrebuilt, @Nonnull File proguardPrebuilt) {
+ super(jillPrebuilt, jackPrebuilt);
+ this.jarjarPrebuilt = jarjarPrebuilt;
+ this.proguardPrebuilt = proguardPrebuilt;
+ }
+
+ @Override
+ @Nonnull
+ public void srcToExe(@Nonnull String classpath, @Nonnull File out, @Nonnull File... sources)
+ throws Exception {
+ try {
+
+ File jarFile = AbstractTestTools.createTempFile("legacyLib", ".jar");
+ File jarFileJarjar = AbstractTestTools.createTempFile("legacyLibJarjar", ".jar");
+ File jarFileProguard = AbstractTestTools.createTempFile("legacyLibProguard", ".jar");
+
+ srcToLib(classpath, jarFile, true /* zipFiles = */, sources);
+
+ if (jarjarRules != null) {
+ processWithJarJar(jarjarRules, jarFile, jarFileJarjar);
+ } else {
+ jarFileJarjar = jarFile;
+ }
+
+ if (proguardFlags.size() > 0) {
+ processWithProguard(classpath, proguardFlags, jarFileJarjar,
+ jarFileProguard);
+ } else {
+ jarFileProguard = jarFileJarjar;
+ }
+
+ File jillLib = AbstractTestTools.createTempFile("jillLib", ".jar");
+ executeJill(jarFileProguard, jillLib, true);
+
+ libToDex(jillLib, out);
+
+ } catch (IOException e) {
+ throw new RuntimeException("Legacy toolchain exited with an error", e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void srcToLib(@Nonnull String classpath, @Nonnull File out, boolean zipFiles,
+ @Nonnull File... sources) throws Exception {
+
+ if (withDebugInfos) {
+ // TODO(jmhenaff): warning log?
+ }
+
+ try {
+ File classesDir;
+ if (zipFiles) {
+ classesDir = AbstractTestTools.createTempDir();
+ } else {
+ classesDir = out;
+ }
+
+ compileWithExternalRefCompiler(sources, classpath, classesDir);
+
+ if (staticLibs.size() > 0) {
+ for (File staticLib : staticLibs) {
+ AbstractTestTools.unzip(staticLib, classesDir);
+ }
+ }
+ if (zipFiles) {
+ AbstractTestTools.createjar(out, classesDir);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Legacy toolchain exited with an error", e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToLib(@Nonnull File in, @Nonnull File out) throws Exception {
+ throw new AssertionError("Not Yet Implemented");
+ }
+
+ private void compileWithExternalRefCompiler(@Nonnull File[] sources,
+ @Nonnull String classpath, @Nonnull File out) {
+
+ List<String> arguments = new ArrayList<String>();
+ String refCompilerPath = System.getenv("REF_JAVA_COMPILER");
+
+ if (refCompilerPath == null) {
+ throw new RuntimeException("REF_JAVA_COMPILER environment variable not set");
+ }
+
+ arguments.add(refCompilerPath.trim());
+
+ addSourceLevel(sourceLevel, arguments);
+
+ if (annotationProcessorClass != null) {
+ arguments.add("-processor");
+ arguments.add(annotationProcessorClass.getName());
+ }
+
+ if (classpath != null) {
+ arguments.add("-classpath");
+ arguments.add(classpath);
+ }
+
+ AbstractTestTools.addFile(arguments, false, sources);
+
+ arguments.add("-d");
+ arguments.add(out.getAbsolutePath());
+
+ ExecuteFile execFile = new ExecuteFile(arguments.toArray(new String[arguments.size()]));
+ execFile.setErr(outRedirectStream);
+ execFile.setOut(errRedirectStream);
+ execFile.setVerbose(true);
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Reference compiler exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running reference compiler", e);
+ }
+ }
+
+ private void processWithJarJar(@Nonnull File jarjarRules,
+ @Nonnull File inJar, @Nonnull File outJar) {
+ String[] args = new String[]{"java", "-jar", jarjarPrebuilt.getAbsolutePath(),
+ "process", jarjarRules.getAbsolutePath(),
+ inJar.getAbsolutePath(), outJar.getAbsolutePath()};
+
+ ExecuteFile execFile = new ExecuteFile(args);
+ execFile.setOut(outRedirectStream);
+ execFile.setErr(errRedirectStream);
+ execFile.setVerbose(true);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("JarJar exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running Jarjar", e);
+ }
+ }
+
+ private void processWithProguard(@Nonnull String bootclasspathStr,
+ @Nonnull List<File> proguardFlags, @Nonnull File inJar, @Nonnull File outJar) {
+
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-jar");
+ args.add(proguardPrebuilt.getAbsolutePath());
+ args.add("-injar");
+ args.add(inJar.getAbsolutePath());
+ args.add("-outjars");
+ args.add(outJar.getAbsolutePath());
+ args.add("-libraryjars");
+ args.add(bootclasspathStr);
+ args.add("-verbose");
+ args.add("-forceprocessing");
+ args.add("-dontoptimize");
+ for (File flags : proguardFlags) {
+ args.add("-include");
+ args.add(flags.getAbsolutePath());
+ }
+
+ ExecuteFile execFile = new ExecuteFile(args.toArray(new String[args.size()]));
+ execFile.setOut(outRedirectStream);
+ execFile.setErr(errRedirectStream);
+ execFile.setVerbose(true);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Proguard exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running Proguard", e);
+ }
+ }
+
+ private static void addSourceLevel(@Nonnull SourceLevel level, @Nonnull List<String> args) {
+ args.add("-source");
+ switch (level) {
+ case JAVA_6:
+ args.add("1.6");
+ break;
+ case JAVA_7:
+ args.add("1.7");
+ break;
+ default:
+ throw new AssertionError("Unkown level: '" + level.toString() + "'");
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java
new file mode 100644
index 0000000..3a5a231
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java
@@ -0,0 +1,293 @@
+/*
+ * 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.test.toolchain;
+
+import com.android.dx.command.dexer.Main.Arguments;
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * The legacy android toolchain.
+ */
+public class LegacyToolchain extends AndroidToolchain {
+
+ @Nonnull
+ private final File legacyCompilerPrebuilt;
+ @Nonnull
+ private final File jarjarPrebuilt;
+ @Nonnull
+ private final File proguardPrebuilt;
+
+ private boolean useDxOptimization = true;
+
+ LegacyToolchain(@Nonnull File legacyCompilerPrebuilt, @Nonnull File jarjarPrebuilt,
+ @Nonnull File proguardPrebuilt) {
+ this.legacyCompilerPrebuilt = legacyCompilerPrebuilt;
+ this.jarjarPrebuilt = jarjarPrebuilt;
+ this.proguardPrebuilt = proguardPrebuilt;
+ }
+
+ @Override
+ @Nonnull
+ public void srcToExe(@Nonnull String classpath, @Nonnull File out,
+ @Nonnull File... sources) throws Exception {
+
+ try {
+
+ File jarFile = AbstractTestTools.createTempFile("legacyLib", ".jar");
+ File jarFileJarjar = AbstractTestTools.createTempFile("legacyLibJarjar", ".jar");
+ File jarFileProguard = AbstractTestTools.createTempFile("legacyLibProguard", ".jar");
+
+ srcToLib(classpath, jarFile, true /* zipFiles = */, sources);
+
+ if (jarjarRules != null) {
+ processWithJarJar(jarjarRules, jarFile, jarFileJarjar);
+ } else {
+ jarFileJarjar = jarFile;
+ }
+
+ if (proguardFlags.size() > 0) {
+ processWithProguard(classpath, proguardFlags, jarFileJarjar,
+ jarFileProguard);
+ } else {
+ jarFileProguard = jarFileJarjar;
+ }
+
+ libToDex(jarFileProguard, out);
+
+ } catch (IOException e) {
+ throw new RuntimeException("Legacy toolchain exited with an error", e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void srcToLib(@Nonnull String classpath, @Nonnull File out,
+ boolean zipFiles, @Nonnull File... sources) throws Exception {
+
+ try {
+ File classesDir;
+ if (zipFiles) {
+ classesDir = AbstractTestTools.createTempDir();
+ } else {
+ classesDir = out;
+ }
+ if (withDebugInfos) {
+ compileWithEcj(sources, classpath, classesDir);
+ } else {
+ compileWithExternalRefCompiler(sources, classpath, classesDir);
+ }
+ if (staticLibs.size() > 0) {
+ for (File staticLib : staticLibs) {
+ AbstractTestTools.unzip(staticLib, classesDir);
+ }
+ }
+ if (zipFiles) {
+ AbstractTestTools.createjar(out, classesDir);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Legacy toolchain exited with an error", e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToDex(@Nonnull File in, @Nonnull File out) throws Exception {
+
+ try {
+ compileWithDx(in, out);
+ } catch (IOException e) {
+ throw new RuntimeException("Legacy toolchain exited with an error", e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public void libToLib(@Nonnull File in, @Nonnull File out) throws Exception {
+ throw new AssertionError("Not Yet Implemented");
+ }
+
+ @Override
+ @Nonnull
+ public File[] getDefaultBootClasspath() {
+ return new File[] {
+ new File(AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/libs/core-stubs-mini.jar"),
+ new File(AbstractTestTools.getJackRootDir(),
+ "toolchain/jack/jack-tests/libs/junit4.jar")
+ };
+ }
+
+ private void processWithJarJar(@Nonnull File jarjarRules,
+ @Nonnull File inJar, @Nonnull File outJar) {
+ String[] args = new String[]{"java", "-jar", jarjarPrebuilt.getAbsolutePath(),
+ "process", jarjarRules.getAbsolutePath(),
+ inJar.getAbsolutePath(), outJar.getAbsolutePath()};
+
+ ExecuteFile execFile = new ExecuteFile(args);
+ execFile.setOut(outRedirectStream);
+ execFile.setErr(errRedirectStream);
+ execFile.setVerbose(true);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("JarJar exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running Jarjar", e);
+ }
+ }
+
+ private void processWithProguard(@Nonnull String bootclasspathStr,
+ @Nonnull List<File> proguardFlags, @Nonnull File inJar, @Nonnull File outJar) {
+
+ List<String> args = new ArrayList<String>();
+ args.add("java");
+ args.add("-jar");
+ args.add(proguardPrebuilt.getAbsolutePath());
+ args.add("-injar");
+ args.add(inJar.getAbsolutePath());
+ args.add("-outjars");
+ args.add(outJar.getAbsolutePath());
+ args.add("-libraryjars");
+ args.add(bootclasspathStr);
+ args.add("-verbose");
+ args.add("-forceprocessing");
+ args.add("-dontoptimize");
+ for (File flags : proguardFlags) {
+ args.add("-include");
+ args.add(flags.getAbsolutePath());
+ }
+
+ ExecuteFile execFile = new ExecuteFile(args.toArray(new String[args.size()]));
+ execFile.setOut(outRedirectStream);
+ execFile.setErr(errRedirectStream);
+ execFile.setVerbose(true);
+
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Proguard exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running Proguard", e);
+ }
+ }
+
+ private void compileWithEcj(@Nonnull File[] sources, @Nonnull String classpath,
+ @Nonnull File out) {
+
+ throw new AssertionError("Not yet implemented");
+ }
+
+ @Override
+ @Nonnull
+ public LegacyToolchain disableDxOptimizations() {
+ useDxOptimization = false;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public LegacyToolchain enableDxOptimizations() {
+ useDxOptimization = true;
+ return this;
+ }
+
+ private static void addSourceLevel(@Nonnull SourceLevel level, @Nonnull List<String> args) {
+ args.add("-source");
+ switch (level) {
+ case JAVA_6:
+ args.add("1.6");
+ break;
+ case JAVA_7:
+ args.add("1.7");
+ break;
+ default:
+ throw new AssertionError("Unkown level: '" + level.toString() + "'");
+ }
+ }
+
+ private void compileWithExternalRefCompiler(@Nonnull File[] sources,
+ @Nonnull String classpath, @Nonnull File out) {
+
+ List<String> arguments = new ArrayList<String>();
+
+ arguments.add(legacyCompilerPrebuilt.getAbsolutePath());
+
+ addSourceLevel(sourceLevel, arguments);
+
+ if (annotationProcessorClass != null) {
+ arguments.add("-processor");
+ arguments.add(annotationProcessorClass.getName());
+ }
+
+ if (classpath != null) {
+ arguments.add("-classpath");
+ arguments.add(classpath);
+ }
+
+ AbstractTestTools.addFile(arguments, false, sources);
+
+ arguments.add("-d");
+ arguments.add(out.getAbsolutePath());
+
+ ExecuteFile execFile = new ExecuteFile(arguments.toArray(new String[arguments.size()]));
+ execFile.setErr(outRedirectStream);
+ execFile.setOut(errRedirectStream);
+ execFile.setVerbose(true);
+ try {
+ if (execFile.run() != 0) {
+ throw new RuntimeException("Reference compiler exited with an error");
+ }
+ } catch (ExecFileException e) {
+ throw new RuntimeException("An error occured while running reference compiler", e);
+ }
+ }
+
+ private void compileWithDx(File in, File out)
+ throws IOException {
+
+ try {
+ System.setOut(outRedirectStream);
+ System.setErr(errRedirectStream);
+
+ Arguments arguments = new Arguments();
+
+ arguments.jarOutput = false;
+ arguments.outName = new File(out, getBinaryFileName()).getAbsolutePath();
+ arguments.optimize = !withDebugInfos && useDxOptimization;
+ // this only means we deactivate the check that no core classes are included
+ arguments.coreLibrary = true;
+ arguments.parse(new String[] {in.getAbsolutePath()});
+
+ int retValue = com.android.dx.command.dexer.Main.run(arguments);
+ if (retValue != 0) {
+ throw new RuntimeException("Dx failed and returned " + retValue);
+ }
+ } finally {
+ System.setOut(stdOut);
+ System.setErr(stdErr);
+ }
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/TestConfigurationException.java b/jack-tests/src/com/android/jack/test/toolchain/TestConfigurationException.java
new file mode 100644
index 0000000..a329b78
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/TestConfigurationException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test.toolchain;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This {@code Exception} is thrown when something is wrong with the configuration
+ * of the test framework (unset variables, ...)
+ */
+public class TestConfigurationException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public TestConfigurationException() {
+ }
+
+ public TestConfigurationException(@Nonnull String message) {
+ super(message);
+ }
+
+ public TestConfigurationException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+
+ public TestConfigurationException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java
new file mode 100644
index 0000000..d35d814
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java
@@ -0,0 +1,154 @@
+/*
+ * 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.test.toolchain;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.processing.Processor;
+
+/**
+ * A toolchain the ouptut of which can be redirected to user defined streams.
+ */
+public abstract class Toolchain implements IToolchain {
+
+ protected boolean withDebugInfos = false;
+
+ @CheckForNull
+ protected Class<? extends Processor> annotationProcessorClass;
+
+ /**
+ * Java source level.
+ */
+ public static enum SourceLevel {
+ JAVA_6,
+ JAVA_7,
+ }
+
+ @Nonnull
+ protected SourceLevel sourceLevel = SourceLevel.JAVA_6;
+ @Nonnull
+ protected List<File> staticLibs = Collections.emptyList();
+ @Nonnull
+ protected List<File> proguardFlags = Collections.emptyList();
+ @CheckForNull
+ protected File jarjarRules;
+
+ @Nonnull
+ protected PrintStream stdOut = System.out;
+ @Nonnull
+ protected PrintStream stdErr = System.err;
+ @Nonnull
+ protected PrintStream outRedirectStream = System.out;
+ @Nonnull
+ protected PrintStream errRedirectStream = System.err;
+
+ Toolchain() {}
+
+ @Override
+ @Nonnull
+ public abstract void srcToExe(@Nonnull String classpath, @Nonnull File out,
+ @Nonnull File... sources) throws Exception;
+
+ @Override
+ @Nonnull
+ public abstract void srcToLib(@Nonnull String classpath, @Nonnull File out,
+ boolean zipFiles, @Nonnull File... sources) throws Exception;
+
+ @Override
+ @Nonnull
+ public abstract void libToDex(@Nonnull File in, @Nonnull File out) throws Exception;
+
+ @Override
+ @Nonnull
+ public abstract void libToLib(@Nonnull File in, @Nonnull File out) throws Exception;
+
+ @Override
+ @Nonnull
+ public Toolchain setWithDebugInfos(boolean withDebugInfos) {
+ this.withDebugInfos = withDebugInfos;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain setAnnotationProcessorClass(
+ @Nonnull Class<? extends Processor> annotationProcessorClass) {
+ this.annotationProcessorClass = annotationProcessorClass;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public Toolchain setSourceLevel(@Nonnull SourceLevel sourceLevel) {
+ this.sourceLevel = sourceLevel;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain addProguardFlags(@Nonnull File... proguardFlags) {
+ if (this.proguardFlags == Collections.EMPTY_LIST) {
+ this.proguardFlags = new ArrayList<File>(proguardFlags.length);
+ }
+ Collections.addAll(this.proguardFlags, proguardFlags);
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain setJarjarRules(@Nonnull File jarjarRules) {
+ this.jarjarRules = jarjarRules;
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain addStaticLibs(@Nonnull File... staticLibs) {
+ if (this.staticLibs == Collections.EMPTY_LIST) {
+ this.staticLibs = new ArrayList<File>(staticLibs.length);
+ }
+ Collections.addAll(this.staticLibs, staticLibs);
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain setOutputStream(@Nonnull OutputStream outputStream) {
+ if (outRedirectStream != null) {
+ outRedirectStream.close();
+ }
+ outRedirectStream = new PrintStream(outputStream);
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public final Toolchain setErrorStream(@Nonnull OutputStream errorStream) {
+ if (errRedirectStream != null) {
+ errRedirectStream.close();
+ }
+ errRedirectStream = new PrintStream(errorStream);
+ return this;
+ }
+}
diff --git a/jack-tests/src/com/android/jack/test/util/BytesStreamSucker.java b/jack-tests/src/com/android/jack/test/util/BytesStreamSucker.java
new file mode 100644
index 0000000..3f74378
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/util/BytesStreamSucker.java
@@ -0,0 +1,74 @@
+/*
+ * 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.test.util;
+
+import com.google.common.io.NullOutputStream;
+
+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 BytesStreamSucker(@Nonnull InputStream is) {
+ this(is, new NullOutputStream(), 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/jack-tests/src/com/android/jack/test/util/CharactersStreamSucker.java b/jack-tests/src/com/android/jack/test/util/CharactersStreamSucker.java
new file mode 100644
index 0000000..7e5b9f2
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/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.test.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+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 InputStream is;
+ @Nonnull
+ private final PrintStream os;
+
+ private final boolean toBeClose;
+
+ public CharactersStreamSucker(
+ @Nonnull InputStream is, @Nonnull PrintStream os, boolean toBeClose) {
+ this.is = is;
+ this.os = os;
+ this.toBeClose = toBeClose;
+ }
+
+ public CharactersStreamSucker(@Nonnull InputStream is, @Nonnull PrintStream os) {
+ this(is, os, false);
+ }
+
+ public CharactersStreamSucker(@Nonnull InputStream is) {
+ this(is, new NullPrintStream(), false);
+ }
+
+ public void suck() throws IOException {
+ int readChar;
+ try {
+ while ((readChar = is.read()) != -1) {
+ os.write(readChar);
+ }
+ } finally {
+ if (toBeClose) {
+ os.close();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/jack-tests/src/com/android/jack/test/util/ExecFileException.java b/jack-tests/src/com/android/jack/test/util/ExecFileException.java
new file mode 100644
index 0000000..8852f85
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/util/ExecFileException.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.test.util;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Exception report during execution of an external process handle by {@link ExecuteFile}
+ */
+public class ExecFileException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ @CheckForNull
+ String errorMsg;
+
+ public ExecFileException() {
+ super();
+ }
+
+ public ExecFileException(@Nonnull String[] cmdLine, @Nonnull Exception e) {
+ super();
+
+ errorMsg = "Error during execution of ";
+
+ for (String arg : cmdLine) {
+ errorMsg += ' ' + arg;
+ }
+
+ errorMsg += ": " + e.getMessage();
+ }
+
+ public ExecFileException(@Nonnull String[] cmdLine, int value) {
+ super();
+
+ errorMsg = "Return value of ";
+
+ for (String arg : cmdLine) {
+ errorMsg += ' ' + arg;
+ }
+
+ errorMsg += " is " + value;
+
+ }
+
+ @Override
+ @CheckForNull
+ public String getMessage() {
+ return errorMsg;
+ }
+} \ No newline at end of file
diff --git a/jack-tests/src/com/android/jack/test/util/ExecuteFile.java b/jack-tests/src/com/android/jack/test/util/ExecuteFile.java
new file mode 100644
index 0000000..1f59b63
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/util/ExecuteFile.java
@@ -0,0 +1,292 @@
+/*
+ * 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.test.util;
+
+import com.android.sched.util.log.LoggerFactory;
+
+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.List;
+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;
+
+ @Nonnull
+ List<String> env = new ArrayList<String>(0);
+
+ @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;
+
+ 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 void addEnvVar(@Nonnull String key, @Nonnull String value) {
+ env.add(key + "=" + value);
+ }
+
+ 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();
+ logger = LoggerFactory.getLogger();
+ }
+
+ 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;
+ logger = LoggerFactory.getLogger();
+ }
+
+ public ExecuteFile(@Nonnull File exec) {
+ cmdLine = new String[1];
+ cmdLine[0] = exec.getAbsolutePath();
+ logger = LoggerFactory.getLogger();
+ }
+
+ public ExecuteFile(@Nonnull String[] cmdLine) {
+ this.cmdLine = cmdLine.clone();
+ logger = LoggerFactory.getLogger();
+ }
+
+ 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]);
+ logger = LoggerFactory.getLogger();
+ }
+
+ public int run() throws ExecFileException {
+ int ret;
+ Process proc = null;
+ Thread suckOut = null;
+ Thread suckErr = null;
+ Thread suckIn = null;
+
+ try {
+ StringBuilder cmdLineBuilder = new StringBuilder();
+ for (String envElt : env) {
+ cmdLineBuilder.append(envElt).append(' ');
+ }
+ 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, env.toArray(new String[env.size()]), 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;
+ } catch (Exception e) {
+ throw new ExecFileException(cmdLine, e);
+ }
+ }
+
+ 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/jack-tests/src/com/android/jack/test/util/NullPrintStream.java b/jack-tests/src/com/android/jack/test/util/NullPrintStream.java
new file mode 100644
index 0000000..730153f
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/util/NullPrintStream.java
@@ -0,0 +1,181 @@
+/*
+ * 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.test.util;
+
+import com.google.common.io.NullOutputStream;
+
+import java.io.PrintStream;
+import java.util.Locale;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Implementation of {@link PrintStream} that simply discards all data.
+ */
+public class NullPrintStream extends PrintStream {
+ public NullPrintStream() {
+ super(new NullOutputStream());
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public boolean checkError() {
+ return false;
+ }
+
+ @Override
+ protected void setError() {
+ }
+
+ @Override
+ protected void clearError() {
+ }
+
+ @Override
+ public void write(int b) {
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ }
+
+ @Override
+ public void print(boolean b) {
+ }
+
+ @Override
+ public void print(char c) {
+ }
+
+ @Override
+ public void print(int i) {
+ }
+
+ @Override
+ public void print(long l) {
+ }
+
+ @Override
+ public void print(float f) {
+ }
+
+ @Override
+ public void print(double d) {
+ }
+
+ @Override
+ public void print(char[] s) {
+ }
+
+ @Override
+ public void print(String s) {
+ }
+
+ @Override
+ public void print(Object obj) {
+ }
+
+ @Override
+ public void println() {
+ }
+
+ @Override
+ public void println(boolean x) {
+ }
+
+ @Override
+ public void println(char x) {
+ }
+
+ @Override
+ public void println(int x) {
+ }
+
+ @Override
+ public void println(long x) {
+ }
+
+ @Override
+ public void println(float x) {
+ }
+
+ @Override
+ public void println(double x) {
+ }
+
+ @Override
+ public void println(char[] x) {
+ }
+
+ @Override
+ public void println(String x) {
+ }
+
+ @Override
+ public void println(Object x) {
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream printf(String format, Object... args) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream printf(Locale l, String format, Object... args) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream format(String format, Object... args) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream format(Locale l, String format, Object... args) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream append(CharSequence csq) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream append(CharSequence csq, int start, int end) {
+ return this;
+ }
+
+ @Override
+ @Nonnull
+ public PrintStream append(char c) {
+ return this;
+ }
+
+}