diff options
author | Jean-Marie Henaff <jmhenaff@google.com> | 2014-05-19 17:34:13 +0200 |
---|---|---|
committer | Jean-Marie Henaff <jmhenaff@google.com> | 2014-10-14 10:35:18 +0200 |
commit | f265ce821c48ed54ad8d00060664b55a8f8e1bb7 (patch) | |
tree | 798f4464e8669ce31fbd5bbe0a2e3404d4e47ff1 | |
parent | 706b60417fe72a4a70bc61da2f915fe3693dd0c2 (diff) | |
download | toolchain_jack-f265ce821c48ed54ad8d00060664b55a8f8e1bb7.zip toolchain_jack-f265ce821c48ed54ad8d00060664b55a8f8e1bb7.tar.gz toolchain_jack-f265ce821c48ed54ad8d00060664b55a8f8e1bb7.tar.bz2 |
WIP Use JUnit for jack-tests.
(cherry picked from commit 452cbd7d69db557ecdbbd20875a669752cf2d9d7)
Change-Id: I96a34b90c9525fa4403f6f940d6fcdf4656722ab
83 files changed, 7901 insertions, 5 deletions
@@ -52,6 +52,7 @@ <property name="jack-tests.dir" value="${jack-project.dir}/jack-tests" /> <property name="antlr.dir" value="${jack-project.dir}/antlr"/> <property name="args4j.dir" value="${jack-project.dir}/args4j"/> + <property name="ddm-lib.dir" value="${jack-project.dir}/ddmlib"/> <property name="dx.dir" value="${jack-project.dir}/dx"/> <property name="ecj.dir" value="${jack-project.dir}/ecj"/> <property name="freemarker.dir" value="${jack-project.dir}/freemarker"/> @@ -275,23 +276,72 @@ <!-- jack-tests --> <!-- ******************* --> <property name="jack-tests.libs.dir" value="${jack-tests.dir}/libs" /> + <property name="jack-tests.build.dir" value="${jack-tests.dir}/build" /> + <property name="jack-tests.dist.dir" value="${jack-tests.dir}/dist" /> + <property name="jack-tests.libname" value="jack-tests-lib.jar" /> <target name="jack-tests-clean"> <delete dir="${jack-tests.libs.dir}" /> </target> - <target name="jack-tests" depends="core-stubs-mini,junit4"> - <!-- project layout --> - <mkdir dir="${jack-tests.libs.dir}" /> - <!-- fetch dependencies --> + <target name="jack-tests-copy-libs" depends="core-stubs-mini,junit4,dx-ref-lib,ddm-lib,ecj, + antlr-rt-lib,guava-lib,dex-lib,jsr305-lib,dexcomparator-lib"> <copy todir="${jack-tests.libs.dir}" flatten="true"> <filelist dir="/" > <file name="${junit4.dist.dir}/${junit4.execname}"/> <file name="${core-stubs-mini.dist.dir}/${core-stubs-mini.libname}"/> + <file name="${dx-ref.dist.dir}/${dx-ref.libname}"/> + <file name="${ddm-lib.dist.dir}/${ddm-lib.libname}"/> + <file name="${ecj.dist.dir}/${ecj.libname}"/> + <file name="${antlr-rt.dist.dir}/${antlr-rt.libname}"/> + <file name="${guava.dist.dir}/${guava.libname}"/> + <file name="${dexlib.dist.dir}/${dexlib.libname}"/> + <file name="${jsr305.dist.dir}/${jsr305.libname}"/> + <file name="${dexcomparator.dist.dir}/${dexcomparator.libname}"/> </filelist> </copy> </target> + <target name="jack-tests" depends="jack-tests-copy-libs"> + <!-- project layout --> + <mkdir dir="${jack-tests.build.outdir}"/> + <mkdir dir="${jack-tests.dist.dir}"/> + <!-- compile --> + <javac + destdir="${jack-tests.build.outdir}" + source="1.5" target="1.5" + debug="true" includeantruntime="false"> + <src path="${jack-tests.dir}/src"/> + <src path="${jack-tests.dir}/tests"/> + <exclude name="com/android/jack/classpath/test002/lib1override/**"/> + <exclude name="com/android/jack/enums/test003/link/Other.java"/> + <exclude name="com/android/jack/enums/test003/link/Values.java"/> + <exclude name="com/android/jack/error/test001/jack/A.java"/> + <exclude name="com/android/jack/error/test002/jack/A.java"/> + <exclude name="com/android/jack/nopackage/jack/**"/> + <exclude name="com/android/jack/java7/boxing/**"/> + <exclude name="com/android/jack/java7/switches/**"/> + <exclude name="com/android/jack/java7/exceptions/**"/> + <exclude name="com/android/jack/java7/trywithresources/**"/> + <exclude name="com/android/jack/java7/parser/**"/> + <exclude name="com/android/jack/jarjar/test003/dontcompile/**"/> + <exclude name="com/android/jack/lookup/test001/liboverride/**"/> + <classpath> + <filelist dir="."> + <!-- <file name="${jsr305.dist.dir}/${jsr305.libname}" /> --> + <file name="${junit4.dist.dir}/${junit4.libname}" /> + <file name="${jackunittests.dist.dir}/${jackunittests.libname}" /> + <file name="${dexcomparator.dist.dir}/${dexcomparator.libname}" /> + <file name="${ddm-lib.dist.dir}/${ddm-lib.libname}" /> + </filelist> + </classpath> + </javac> + <!-- package --> + <jar destfile="${jack-tests.dist.dir}/${jack-tests.libname}" + basedir="${jack-tests.build.outdir}" + includes="**"/> + </target> + <!-- ******************* --> <!-- antlr --> @@ -344,6 +394,22 @@ <!-- ******************* --> + <!-- ddmlib --> + <!-- ******************* --> + <property name="ddm-lib.libname" value="ddmlib.jar"/> + <property name="ddm-lib.dist.dir" value="${ddm-lib.dir}/dist"/> + + <target name="ddm-lib-clean"> + <delete dir="${ddm-lib.dist.dir}"/> + </target> + + <target name="ddm-lib"> + <mkdir dir="${ddm-lib.dist.dir}"/> + <copy file="${ddm-lib.dir}/ddmlib.jar" tofile="${ddm-lib.dist.dir}/${ddm-lib.libname}"/> + </target> + + + <!-- ******************* --> <!-- dx-lib --> <!-- ******************* --> <property name="dx.build.dir" value="${dx.dir}/build"/> diff --git a/ddmlib/README.android b/ddmlib/README.android new file mode 100644 index 0000000..9729ef7 --- /dev/null +++ b/ddmlib/README.android @@ -0,0 +1,9 @@ +URL: https://android.git.corp.google.com/platform/prebuilts/devtools/+/42f8ef3570f43414858a974ddd593ac13ab02163/tools/lib/ddmlib.jar +Tag: AOSP master, prebuilts/devtools +License: Apache 2 +Description: A library to control android/host operations. +Local Modifications: None. + +Commit 42f8ef3570f43414858a974ddd593ac13ab02163 +from branch master + diff --git a/ddmlib/ddmlib.jar b/ddmlib/ddmlib.jar Binary files differnew file mode 100644 index 0000000..5b9e8bb --- /dev/null +++ b/ddmlib/ddmlib.jar diff --git a/hamcrest-core/Android.mk b/hamcrest-core/Android.mk index 547bdad..e41fbde 100644 --- a/hamcrest-core/Android.mk +++ b/hamcrest-core/Android.mk @@ -39,3 +39,13 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_HOST_JAVA_LIBRARY) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_MODULE := hamcrest-core-target-jack + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_JAVA_LIBRARY) + diff --git a/jack-tests/.checkstyle b/jack-tests/.checkstyle new file mode 100644 index 0000000..5cc9f9e --- /dev/null +++ b/jack-tests/.checkstyle @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false"> + <local-check-config name="Jack Tests CheckStyle" location="jackstyle.xml" type="project" description=""> + <additional-data name="protect-config-file" value="true"/> + </local-check-config> + <fileset name="all" enabled="false" check-config-name="Sun Checks" local="false"> + <file-match-pattern match-pattern="." include-pattern="true"/> + </fileset> + <fileset name="Jack Tests Checkstyle" enabled="true" check-config-name="Jack Tests CheckStyle" local="true"> + <file-match-pattern match-pattern="^src.*\.java$" include-pattern="true"/> + </fileset> +</fileset-config> diff --git a/jack-tests/.classpath b/jack-tests/.classpath index ad8cdf8..b61e0f4 100644 --- a/jack-tests/.classpath +++ b/jack-tests/.classpath @@ -4,5 +4,15 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> <classpathentry excluding="com/android/jack/classpath/test002/lib1override/|com/android/jack/compiletime/|com/android/jack/enums/test003/link/Other.java|com/android/jack/enums/test003/link/Values.java|com/android/jack/error/test001/jack/A.java|com/android/jack/error/test002/jack/A.java|com/android/jack/java7/boxing/|com/android/jack/java7/exceptions/|com/android/jack/java7/parser/|com/android/jack/java7/switches/|com/android/jack/java7/trywithresources/|com/android/jack/nopackage/jack/|com/android/jack/lookup/test001/liboverride/|com/android/jack/jarjar/test003/dontcompile/" kind="src" path="tests"/> <classpathentry kind="lib" path="libs/junit4.jar"/> + <classpathentry kind="lib" path="libs/antlr-runtime-lib.jar"/> + <classpathentry kind="lib" path="libs/dx-ref.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/dexcomparator"/> + <classpathentry kind="lib" path="libs/ecj.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/Scheduler"/> + <classpathentry kind="lib" path="libs/guava-lib.jar"/> + <classpathentry kind="lib" path="libs/dex-lib.jar"/> + <classpathentry kind="lib" path="libs/jsr305-lib.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/Jack"/> + <classpathentry kind="lib" path="libs/ddmlib.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/jack-tests/.project b/jack-tests/.project index f4f439a..0461f29 100644 --- a/jack-tests/.project +++ b/jack-tests/.project @@ -10,8 +10,14 @@ <arguments> </arguments> </buildCommand> + <buildCommand> + <name>net.sf.eclipsecs.core.CheckstyleBuilder</name> + <arguments> + </arguments> + </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> + <nature>net.sf.eclipsecs.core.CheckstyleNature</nature> </natures> </projectDescription> diff --git a/jack-tests/jackstyle.xml b/jack-tests/jackstyle.xml new file mode 100644 index 0000000..3940973 --- /dev/null +++ b/jack-tests/jackstyle.xml @@ -0,0 +1,314 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE module PUBLIC + "-//Puppy Crawl//DTD Check Configuration 1.3//EN" + "http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> + +<!-- This is a checkstyle configuration file. For descriptions of +what the following rules do, please see the checkstyle configuration +page at http://checkstyle.sourceforge.net/config.html --> + +<!-- Checks with numbered comments refer to recommendations made +by Joshua Bloch in his book Effective Java --> + +<module name="Checker"> + <property name="charset" value="UTF-8"/> + <module name="FileTabCharacter"> + <!-- Checks that there are no tab characters in the file. + --> + </module> + + <module name="RegexpSingleline"> + <!-- Checks that FIXME is not used in comments. TODO is preferred. + --> + <property name="format" value="((//.*)|(\*.*))FIXME" /> + <property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' /> + </module> + + <module name="RegexpSingleline"> + <!-- Checks that TODOs are properly formatted. + + The (?<!TODO\(.{0,100}) makes the regex ignore any secondary TODO's on the line + so that things like //TODO(bob): remove this TODO on 1/1/2020 don't trigger a warning + because of the second TODO. (The {0,100} is because java doesn't recoginize arbitrary + length look backs, but we know each java line should be < 100 chars.) + --> + <property name="format" value="((//.*)|(\*.*))(?<!TODO\(.{0,100})(TODO[^(])|(TODO\([^)]*$)" /> + <property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' /> + </module> + + + <!-- All Java AST specific tests live under TreeWalker module. --> + <module name="TreeWalker"> + + <!-- + + IMPORT CHECKS + + --> + + <module name="RedundantImport"> + <property name="severity" value="error"/> + </module> + + <module name="AvoidStarImport"> + <property name="severity" value="error"/> + </module> + + <module name="UnusedImports"> + <!-- DPL is a notable violator of this rule. --> + <property name="severity" value="error"/> + <!-- Imports used only in Javadoc are tolerated. --> + <property name="processJavadoc" value="true"/> + <message + key="import.unused" + value="Unused import: {0}." /> + </module> + + <module name="ImportOrder"> + <!-- Checks for out of order import statements. --> + <property name="severity" value="warning"/> + <property name="groups" value="com.google,*,java,javax"/> + <!-- This ensures that static imports go first. --> + <property name="option" value="top"/> + <property name="tokens" value="STATIC_IMPORT, IMPORT"/> + </module> + + <!-- + + JAVADOC CHECKS + + --> + + <module name="JavadocType"> + <!-- Item 28 - Write doc comments for all exposed API elements. --> + <!-- Ensure all classes with visability greater than or equal to + protected have class level documentation. --> + <property name="scope" value="protected"/> + <property name="severity" value="error"/> + <!-- Style guide doesn't prohibit custom tags. Typos will be caught by other tools. --> + <property name="allowUnknownTags" value="true"/> + <property name="allowMissingParamTags" value="true"/> + <message key="javadoc.missing" + value="Missing a Javadoc comment."/> + </module> + + <!-- + + NAMING CHECKS + + --> + + <!-- Item 38 - Adhere to generally accepted naming conventions --> + + <module name="PackageName"> + <!-- Validates identifiers for package names against the + supplied expression. --> + <!-- Here the default checkstyle rule restricts package name parts to + seven characters, this is not in line with common practice at Google. + --> + <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/> + <property name="severity" value="warning"/> + </module> + + <module name="TypeNameCheck"> + <metadata name="altname" value="TypeName"/> + <property name="severity" value="warning"/> + </module> + + <module name="StaticVariableNameCheck"> + <!-- Validates static, non-final fields against the supplied + expression "^[a-z][a-zA-Z0-9]*?$". --> + <metadata name="altname" value="StaticVariableName"/> + <property name="applyToPublic" value="true"/> + <property name="applyToProtected" value="true"/> + <property name="applyToPackage" value="true"/> + <property name="applyToPrivate" value="true"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*?$"/> + <property name="severity" value="warning"/> + </module> + + <module name="MemberNameCheck"> + <!-- Validates non-static members against the supplied expression. --> + <metadata name="altname" value="MemberName"/> + <property name="applyToPublic" value="true"/> + <property name="applyToProtected" value="true"/> + <property name="applyToPackage" value="true"/> + <property name="applyToPrivate" value="true"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*?$"/> + <property name="severity" value="warning"/> + </module> + + <module name="MethodNameCheck"> + <!-- Validates identifiers for method names. --> + <metadata name="altname" value="MethodName"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*([a-zA-Z0-9]+)*$"/> + <property name="severity" value="warning"/> + </module> + + <module name="ParameterName"> + <!-- Validates identifiers for method parameters against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + <module name="LocalFinalVariableName"> + <!-- Validates identifiers for local final variables against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + <module name="LocalVariableName"> + <!-- Validates identifiers for local variables against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + + <!-- + + LENGTH and CODING CHECKS + + --> + + <module name="LineLength"> + <!-- Checks if a line is too long. --> + <property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="100"/> + <property name="severity" value="error"/> + + <!-- + The default ignore pattern exempts the following elements: + - import statements + - long URLs inside comments + --> + + <property name="ignorePattern" + value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}" + default="^(package .*;\s*)|(import .*;\s*)|( *\* *https?://.*)|(\s*@[\w\.\$]+::\w+(?:\([^\(]*\)|\(\)\(\))?[,;]?)|(\s+\* \{@(link|see) [^\s][^\}]*\}[\.,;]?)$"/> + </module> + + <module name="LeftCurly"> + <!-- Checks for placement of the left curly brace ('{'). --> + <property name="severity" value="warning"/> + </module> + + <module name="RightCurly"> + <!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on + the same line. e.g., the following example is fine: + <pre> + if { + ... + } else + </pre> + --> + <!-- This next example is not fine: + <pre> + if { + ... + } + else + </pre> + --> + <property name="option" value="same"/> + <property name="severity" value="warning"/> + </module> + + <!-- Checks for braces around if and else blocks --> + <module name="NeedBraces"> + <property name="severity" value="warning"/> + <property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/> + </module> + + <module name="UpperEll"> + <!-- Checks that long constants are defined with an upper ell.--> + <property name="severity" value="error"/> + </module> + + <module name="FallThrough"> + <!-- Warn about falling through to the next case statement. Similar to + javac -Xlint:fallthrough, but the check is suppressed if there is a single-line comment + on the last non-blank line preceding the fallen-into case. + --> + <property name="reliefPattern" + value=".*"/> + <property name="severity" value="error"/> + </module> + + + <!-- + + MODIFIERS CHECKS + + --> + + <module name="ModifierOrder"> + <!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and + 8.4.3. The prescribed order is: + public, protected, private, abstract, static, final, transient, volatile, + synchronized, native, strictfp + --> + </module> + + + <!-- + + WHITESPACE CHECKS + + --> + + <module name="WhitespaceAround"> + <!-- Checks that various tokens are surrounded by whitespace. + This includes most binary operators and keywords followed + by regular or curly braces. + --> + <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, + BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, + EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, + LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, + LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, + MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, + SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/> + <property name="severity" value="error"/> + </module> + + <module name="WhitespaceAfter"> + <!-- Checks that commas, semicolons and typecasts are followed by + whitespace. + --> + <property name="tokens" value="COMMA, SEMI, TYPECAST"/> + </module> + + <module name="NoWhitespaceAfter"> + <!-- Checks that there is no whitespace after various unary operators. + Linebreaks are allowed. + --> + <property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, + UNARY_PLUS"/> + <property name="allowLineBreaks" value="true"/> + <property name="severity" value="error"/> + </module> + + <module name="NoWhitespaceBefore"> + <!-- Checks that there is no whitespace before various unary operators. + Linebreaks are allowed. + --> + <property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/> + <property name="allowLineBreaks" value="true"/> + <property name="severity" value="error"/> + </module> + + <module name="ParenPad"> + <!-- Checks that there is no whitespace before close parens or after + open parens. + --> + <property name="severity" value="warning"/> + </module> + + <!-- + + MISC CHECKS + + --> + + </module> +</module> + diff --git a/jack-tests/prebuilts/core-hostdex.jar b/jack-tests/prebuilts/core-hostdex.jar Binary files differnew file mode 100644 index 0000000..05de7fb --- /dev/null +++ b/jack-tests/prebuilts/core-hostdex.jar diff --git a/jack-tests/prebuilts/jarjar.jar b/jack-tests/prebuilts/jarjar.jar Binary files differnew file mode 100644 index 0000000..89390bf --- /dev/null +++ b/jack-tests/prebuilts/jarjar.jar diff --git a/jack-tests/prebuilts/junit4-hostdex.jar b/jack-tests/prebuilts/junit4-hostdex.jar Binary files differnew file mode 100644 index 0000000..cb4a0b7 --- /dev/null +++ b/jack-tests/prebuilts/junit4-hostdex.jar diff --git a/jack-tests/prebuilts/proguard.jar b/jack-tests/prebuilts/proguard.jar Binary files differnew file mode 100644 index 0000000..56d68ba --- /dev/null +++ b/jack-tests/prebuilts/proguard.jar 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; + } + +} diff --git a/jack-tests/test-exit-status.sh b/jack-tests/test-exit-status.sh new file mode 100755 index 0000000..2eaccfe --- /dev/null +++ b/jack-tests/test-exit-status.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# 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. + +"$@" +echo $? > /data/jack-tests/exitStatus + diff --git a/jack-tests/tests.properties b/jack-tests/tests.properties new file mode 100644 index 0000000..34ce165 --- /dev/null +++ b/jack-tests/tests.properties @@ -0,0 +1,12 @@ +reference.toolchain=legacy +candidate.toolchain=jack-api +#candidate.toolchain=jill-legacy +toolchain.prebuilt.jack=toolchain/jack/jack/dist/jack.jar +toolchain.prebuilt.jill=toolchain/jill/jill/dist/jill.jar +toolchain.prebuilt.jarjar=toolchain/jack/jack-tests/prebuilts/jarjar.jar +toolchain.prebuilt.proguard=toolchain/jack/jack-tests/prebuilts/proguard.jar +toolchain.prebuilt.legacy-java-compiler=/usr/lib/jvm/java-6-sun/bin/javac +runtime.list=dalvik-fast-host,art-host,dalvik-fast-device +runtime.location.dalvik-fast-host=/disk2/tmp/dalvik-host.rt +runtime.location.dalvik-jit-host=/disk2/tmp/dalvik-host.rt +runtime.location.art-host=/disk2/tmp/art-host.rt diff --git a/jack-tests/tests/com/android/jack/AllTests.java b/jack-tests/tests/com/android/jack/AllTests.java new file mode 100644 index 0000000..091867b --- /dev/null +++ b/jack-tests/tests/com/android/jack/AllTests.java @@ -0,0 +1,54 @@ +/* + * 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; + +import com.android.jack.annotation.AnnotationTests; +import com.android.jack.arithmetic.ArithmeticTests; +import com.android.jack.classpath.ClasspathTest; +import com.android.jack.enums.EnumTests; +import com.android.jack.error.AnnotationProcessorErrorTest; +import com.android.jack.error.CommandLineErrorTest; +import com.android.jack.error.FileAccessErrorTest; +import com.android.jack.experimenal.incremental.DependenciesTests005; +import com.android.jack.jarjar.JarjarTests; +import com.android.jack.shrob.ObfuscationWithoutMappingTests; +import com.android.jack.tools.merger.MergerAllTests; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Test suite containing all tests (except for regression tests that must be run from the command + * line). + */ +@RunWith(Suite.class) +@SuiteClasses(value = { + AnnotationTests.class, + ArithmeticTests.class, + ClasspathTest.class, + DependenciesTests005.class, + EnumTests.class, + JarjarTests.class, + MergerAllTests.class, + ObfuscationWithoutMappingTests.class, + AnnotationProcessorErrorTest.class, + FileAccessErrorTest.class, + CommandLineErrorTest.class + }) +public class AllTests { +} diff --git a/jack-tests/tests/com/android/jack/AllWithRegressionTests.java b/jack-tests/tests/com/android/jack/AllWithRegressionTests.java new file mode 100644 index 0000000..4b89472 --- /dev/null +++ b/jack-tests/tests/com/android/jack/AllWithRegressionTests.java @@ -0,0 +1,35 @@ +/* + * 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; + + +import com.android.jack.test.category.RuntimeRegressionTest; + +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Categories.ExcludeCategory; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Test suite containing tests that are too time-consuming and cannot be run before submitting each + * CL. + */ +@RunWith(Categories.class) +@ExcludeCategory(RuntimeRegressionTest.class) +@SuiteClasses(value = {AllTests.class, RegressionTests.class}) +public class AllWithRegressionTests { +} diff --git a/jack-tests/tests/com/android/jack/RegressionTests.java b/jack-tests/tests/com/android/jack/RegressionTests.java new file mode 100644 index 0000000..3a7ecc6 --- /dev/null +++ b/jack-tests/tests/com/android/jack/RegressionTests.java @@ -0,0 +1,56 @@ +/* + * 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; + +import com.android.jack.annotation.AnnotationTests; +import com.android.jack.arithmetic.ArithmeticTests; +import com.android.jack.test.helper.RuntimeTestHelper; +import com.android.jack.test.runtime.RuntimeTest; +import com.android.jack.test.runtime.RuntimeTestInfo; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +public class RegressionTests { + + public RuntimeTest[] tests = { + new AnnotationTests(), + new ArithmeticTests() + }; + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void runRegressionTests() throws Exception { + List<RuntimeTestInfo> rtTestInfos = new ArrayList<RuntimeTestInfo>(); + + for (RuntimeTest test : tests) { + for (RuntimeTestInfo testInfos : test.getRuntimeTestInfos()) { + rtTestInfos.add(testInfos); + } + } + new RuntimeTestHelper(rtTestInfos.toArray(new RuntimeTestInfo[rtTestInfos.size()])) + .compileAndRunTest(); + } + +} diff --git a/jack-tests/tests/com/android/jack/annotation/AnnotationTests.java b/jack-tests/tests/com/android/jack/annotation/AnnotationTests.java new file mode 100644 index 0000000..e68228e --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/AnnotationTests.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.annotation; + +import com.android.jack.Main; +import com.android.jack.test.category.RuntimeRegressionTest; +import com.android.jack.test.helper.CheckDexStructureTestHelper; +import com.android.jack.test.helper.RuntimeTestHelper; +import com.android.jack.test.runtime.RuntimeTest; +import com.android.jack.test.runtime.RuntimeTestInfo; +import com.android.jack.test.toolchain.AbstractTestTools; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.File; + +public class AnnotationTests extends RuntimeTest { + + private static final File ANNOTATION001_PATH = + AbstractTestTools.getTestRootDir("com.android.jack.annotation.test001.jack"); + + private RuntimeTestInfo TEST001 = new RuntimeTestInfo( + AbstractTestTools.getTestRootDir("com.android.jack.annotation.test001"), + "com.android.jack.annotation.test001.dx.Tests"); + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void checkStructure() throws Exception { + CheckDexStructureTestHelper env = + new CheckDexStructureTestHelper(new File(ANNOTATION001_PATH, "Annotation2.java")); + env.setWithDebugInfo(true); + env.compare(); + } + + @Test + @Category(RuntimeRegressionTest.class) + public void runtimeTest001() throws Exception { + new RuntimeTestHelper(TEST001).compileAndRunTest(); + } + + @Override + protected void fillRtTestInfos() { + rtTestInfos.add(TEST001); + } + +} diff --git a/jack-tests/tests/com/android/jack/arithmetic/ArithmeticTests.java b/jack-tests/tests/com/android/jack/arithmetic/ArithmeticTests.java new file mode 100644 index 0000000..1b6ee1a --- /dev/null +++ b/jack-tests/tests/com/android/jack/arithmetic/ArithmeticTests.java @@ -0,0 +1,50 @@ +/* + * 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.arithmetic; + +import com.android.jack.test.category.RuntimeRegressionTest; +import com.android.jack.test.helper.RuntimeTestHelper; +import com.android.jack.test.runtime.RuntimeTest; +import com.android.jack.test.runtime.RuntimeTestInfo; +import com.android.jack.test.toolchain.AbstractTestTools; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +public class ArithmeticTests extends RuntimeTest { + + private RuntimeTestInfo TEST001 = new RuntimeTestInfo( + AbstractTestTools.getTestRootDir("com.android.jack.arithmetic.test001"), + "com.android.jack.arithmetic.test001.dx.Tests"); + + @BeforeClass + public static void setUpClass() { + ArithmeticTests.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + @Category(RuntimeRegressionTest.class) + public void test001() throws Exception { + new RuntimeTestHelper(TEST001).compileAndRunTest(); + } + + @Override + protected void fillRtTestInfos() { + rtTestInfos.add(TEST001); + } +} diff --git a/jack-tests/tests/com/android/jack/classpath/ClasspathTest.java b/jack-tests/tests/com/android/jack/classpath/ClasspathTest.java new file mode 100644 index 0000000..8fde038 --- /dev/null +++ b/jack-tests/tests/com/android/jack/classpath/ClasspathTest.java @@ -0,0 +1,77 @@ +/* + * 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.classpath; + +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.IToolchain; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; + +public class ClasspathTest { + + @BeforeClass + public static void setUpClass() { + ClasspathTest.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void test001() throws Exception { + File libOut = AbstractTestTools.createTempDir(); + + IToolchain toolchain = AbstractTestTools.getCandidateToolchain(); + toolchain.srcToLib(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()), + libOut, false, + new File(AbstractTestTools.getTestRootDir("com.android.jack.classpath.test001"), "lib")); + + File testOut = AbstractTestTools.createTempDir(); + toolchain.srcToLib(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()) + + File.pathSeparatorChar + libOut.getAbsolutePath(), testOut, false, + new File(AbstractTestTools.getTestRootDir("com.android.jack.classpath.test001"), "jack")); + } + + @Test + public void test002() throws Exception { + IToolchain toolchain = AbstractTestTools.getCandidateToolchain(); + + File testFolder = AbstractTestTools.getTestRootDir("com.android.jack.classpath.test002"); + File outFolder = AbstractTestTools.createTempDir(); + + File lib1Out = AbstractTestTools.createDir(outFolder, "lib1"); + toolchain.srcToLib(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()), + lib1Out, + /* zipFiles = */ false, new File(testFolder, "lib1")); + + File lib1BisOut = AbstractTestTools.createDir(outFolder, "lib1override"); + toolchain.srcToLib(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()), + lib1BisOut, + /* zipFiles = */false, new File(testFolder, "lib1override")); + + File lib2Out = AbstractTestTools.createDir(outFolder, "lib2"); + toolchain.srcToLib(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()) + + File.pathSeparatorChar + lib1Out.getAbsolutePath(), lib2Out, + /* zipFiles = */false, new File(testFolder, "lib2")); + + toolchain.addStaticLibs(lib2Out); + toolchain.srcToExe(AbstractTestTools.getClasspathAsString(toolchain.getDefaultBootClasspath()) + + File.pathSeparatorChar + lib1BisOut.getAbsolutePath(), outFolder, + new File(testFolder, "jack")); + + } +} diff --git a/jack-tests/tests/com/android/jack/enums/EnumTests.java b/jack-tests/tests/com/android/jack/enums/EnumTests.java new file mode 100644 index 0000000..361e6a6 --- /dev/null +++ b/jack-tests/tests/com/android/jack/enums/EnumTests.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.enums; + +import com.android.jack.test.helper.RuntimeTestHelper; +import com.android.jack.test.runtime.RuntimeTestInfo; +import com.android.jack.test.toolchain.AbstractTestTools; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +public class EnumTests { + + private File baseDir; + + @Before + public void setUp() { + baseDir = AbstractTestTools.getTestRootDir("com.android.jack.enums.test003"); + } + + @Test + public void compileAndRunTest() throws Exception { + new RuntimeTestHelper(new RuntimeTestInfo(baseDir, "com.android.jack.enums.test003.dx.Tests")) + .compileAndRunTest(); + } + +} diff --git a/jack-tests/tests/com/android/jack/enums/test003/test.properties b/jack-tests/tests/com/android/jack/enums/test003/test.properties new file mode 100644 index 0000000..e3646b8 --- /dev/null +++ b/jack-tests/tests/com/android/jack/enums/test003/test.properties @@ -0,0 +1,4 @@ +rt.args.DalvikRunnerHost=-Xdexopt:none -Xverify:none +rt.args.DalvikRunnerDevice=-Xdexopt:none -Xverify:none +rt.args.ArtRunnerHost=-Xdexopt:none -Xverify:none +rt.args.ArtRunnerDevice=-Xdexopt:none -Xverify:none diff --git a/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java b/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java new file mode 100644 index 0000000..fea5b18 --- /dev/null +++ b/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java @@ -0,0 +1,241 @@ +/* + * 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.error; + +import com.google.common.io.Files; + +import com.android.jack.JackUserException; +import com.android.jack.Main; +import com.android.jack.errorhandling.annotationprocessor.ResourceAnnotationProcessor; +import com.android.jack.errorhandling.annotationprocessor.ResourceAnnotationTest; +import com.android.jack.errorhandling.annotationprocessor.SourceAnnotationProcessor; +import com.android.jack.errorhandling.annotationprocessor.SourceAnnotationTest; +import com.android.jack.errorhandling.annotationprocessor.SourceErrorAnnotationTest; +import com.android.jack.frontend.FrontendCompilationException; +import com.android.jack.test.helper.ErrorTestHelper; +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.JackApiToolchain; + +import junit.framework.Assert; + +import org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.DexFile; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.LineNumberReader; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * JUnit test checking Jack behavior when using annotation processor. + */ +public class AnnotationProcessorErrorTest { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + /** + * Checks that compilation fails correctly when annotation processor is called without specifying + * output folder. + */ + @Test + public void testAnnotationProcessorError001() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + AbstractTestTools.createJavaFile(te.getSourceFolder(),"jack.incremental", "A.java", + "package jack.incremental; \n"+ + "public class A {} \n"); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); + + try { + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + te.getOutputDexFolder(), te.getSourceFolder()); + Assert.fail(); + } catch (JackUserException e) { + // Failure is ok since output for annotation processor is not specify. + Assert.assertTrue(e.getMessage().contains("Unknown location")); + } + } + + /** + * Checks that compilation succeed when running annotation processor to generate resource file. + */ + @Test + public void testAnnotationProcessorError002() throws Exception { + runAnnotProcBuildingResource(new ErrorTestHelper()); + } + + /** + * Checks that last compilation failed since the resource created by annotation processor already + * exist. + */ + @Test + public void testAnnotationProcessorError003() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + runAnnotProcBuildingResource(te); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); + jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + jackApiToolchain.setErrorStream(errOut); + + try { + + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + te.getJackFolder(), te.getOutputDexFolder(), te.getSourceFolder()); + + Assert.fail(); + } catch (FrontendCompilationException e) { + // Failure is ok since created file already exists + } finally { + Assert.assertTrue(errOut.toString().contains("Resource already created")); + } + } + + /** + * Checks that compilation failed since the source file generated by the annotation processor + * does not compile. + */ + @Test + public void testAnnotationProcessorError004() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + buildAnnotationRequiredByAnnotationProc(te, new Class<?>[] {SourceAnnotationTest.class, + SourceErrorAnnotationTest.class}); + + AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", "package jack.incremental;\n" + + "import " + SourceErrorAnnotationTest.class.getName() + ";\n" + + "@" + SourceErrorAnnotationTest.class.getSimpleName() + "\n" + + "public class A {}\n"); + + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.setAnnotationProcessorClass(SourceAnnotationProcessor.class); + jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + jackApiToolchain.setErrorStream(errOut); + + try { + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + te.getJackFolder(), te.getOutputDexFolder(), te.getSourceFolder()); + Assert.fail(); + } catch (FrontendCompilationException ex) { + // Failure is ok since source generated by annotation processor does not compile. + } finally { + Assert.assertTrue(errOut.toString().contains("Syntax error on tokens, delete these tokens")); + } + } + + /** + * Checks that compilation succeed to compile source file generated by the annotation processor. + */ + @Test + public void testAnnotationProcessorError005() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + buildAnnotationRequiredByAnnotationProc(te, new Class<?>[] {SourceAnnotationTest.class, + SourceErrorAnnotationTest.class}); + + AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", "package jack.incremental;\n" + + "import " + SourceAnnotationTest.class.getName() + ";\n" + + "@" + SourceAnnotationTest.class.getSimpleName() + "\n" + + "public class A {}\n"); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.setAnnotationProcessorClass(SourceAnnotationProcessor.class); + jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + + File dexOutput = te.getOutputDexFolder(); + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + te.getJackFolder(), dexOutput, te.getSourceFolder()); + + DexFile dexFile = new DexFile(new File(dexOutput, jackApiToolchain.getBinaryFileName())); + List<String> sourceFileInDex = new ArrayList<String>(); + for (ClassDefItem classDef : dexFile.ClassDefsSection.getItems()) { + sourceFileInDex.add(classDef.getSourceFile().getStringValue()); + } + + Assert.assertTrue(sourceFileInDex.contains("ADuplicated.java")); + Assert.assertTrue(sourceFileInDex.contains("A.java")); + } + + private void runAnnotProcBuildingResource(@Nonnull ErrorTestHelper te) throws Exception { + + buildAnnotationRequiredByAnnotationProc(te, new Class<?>[] {ResourceAnnotationTest.class}); + + AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", "package jack.incremental;\n" + + "import " + ResourceAnnotationTest.class.getName() + ";\n" + + "@" + ResourceAnnotationTest.class.getSimpleName() + "\n" + + "public class A {}\n"); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); + jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + te.getJackFolder(), te.getOutputDexFolder(), te.getSourceFolder()); + + File discoverFile = new File(te.getTestingFolder(), ResourceAnnotationProcessor.FILENAME); + Assert.assertTrue(discoverFile.exists()); + LineNumberReader lnr = new LineNumberReader(new FileReader(discoverFile)); + Assert.assertEquals(ResourceAnnotationTest.class.getName(), lnr.readLine()); + Assert.assertEquals("jack.incremental.A", lnr.readLine()); + Assert.assertNull(lnr.readLine()); + lnr.close(); + } + + private void buildAnnotationRequiredByAnnotationProc(@Nonnull ErrorTestHelper te, + Class<?>[] annotationClasses) throws Exception { + File targetAnnotationFileFolder = + new File(te.getSourceFolder(), "com/android/jack/errorhandling/annotationprocessor/"); + if (!targetAnnotationFileFolder.mkdirs()) { + Assert.fail("Fail to create folder " + targetAnnotationFileFolder.getAbsolutePath()); + } + + for (Class<?> annotationClass : annotationClasses) { + Files.copy(new File(AbstractTestTools.getJackRootDir(), + "toolchain/jack/jack/tests/com/android/jack/errorhandling/annotationprocessor/" + + annotationClass.getSimpleName() + ".java"), new File( + targetAnnotationFileFolder, annotationClass.getSimpleName() + ".java")); + } + + // Compile annotation to a jack file + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + jackApiToolchain.srcToLib( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + te.getJackFolder(), false /* zipFiles = */, te.getSourceFolder()); + + AbstractTestTools.deleteTempDir(te.getSourceFolder()); + } +} diff --git a/jack-tests/tests/com/android/jack/error/CommandLineErrorTest.java b/jack-tests/tests/com/android/jack/error/CommandLineErrorTest.java new file mode 100644 index 0000000..3e72329 --- /dev/null +++ b/jack-tests/tests/com/android/jack/error/CommandLineErrorTest.java @@ -0,0 +1,95 @@ +/* + * 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.error; + +import com.android.jack.IllegalOptionsException; +import com.android.jack.Main; +import com.android.jack.NothingToDoException; +import com.android.jack.test.helper.ErrorTestHelper; +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.JackApiToolchain; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; + +/** + * JUnit test checking Jack behavior on exceptions. + */ +public class CommandLineErrorTest { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + /** + * Checks that compilation fails correctly when an unsupported options is passed to ecj. + */ + @Test + public void testCommandLineError001() throws Exception { + ErrorTestHelper ite = new ErrorTestHelper(); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + jackApiToolchain.setErrorStream(errOut); + jackApiToolchain.addEcjArgs("-unsupported"); + try { + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + ite.getJackFolder(), ite.getOutputDexFolder(), + ite.getSourceFolder()); + Assert.fail(); + } catch (IllegalOptionsException e) { + // Failure is ok since a bad options is passed to ecj. + } finally { + Assert.assertEquals("", errOut.toString()); + } + } + + /** + * Checks that compilation fails correctly when no source files are passed to ecj. + */ + @Test + public void testCommandLineError002() throws Exception { + ErrorTestHelper ite = new ErrorTestHelper(); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + jackApiToolchain.setErrorStream(errOut); + jackApiToolchain.setOutputStream(out); + + try { + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + ite.getJackFolder(), ite.getOutputDexFolder(), + ite.getSourceFolder()); + Assert.fail(); + } catch (NothingToDoException e) { + // Failure is ok since there is no source files. + } finally { + Assert.assertEquals("", errOut.toString()); + Assert.assertTrue(out.toString().contains("Usage:")); + } + } + +} diff --git a/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java b/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java new file mode 100644 index 0000000..f9ce418 --- /dev/null +++ b/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java @@ -0,0 +1,180 @@ +/* + * 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.error; + +import com.android.jack.JackUserException; +import com.android.jack.Main; +import com.android.jack.frontend.FrontendCompilationException; +import com.android.jack.load.JackLoadingException; +import com.android.jack.test.helper.ErrorTestHelper; +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.JackApiToolchain; +import com.android.sched.util.config.PropertyIdException; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; + +/** + * JUnit test checking Jack behavior on file access error. + */ +public class FileAccessErrorTest { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + /** + * Checks that compilation fails correctly when folder to generate jack files is not readable. + */ + @Test + public void testFileAccessError001() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", + "package jack.incremental; \n"+ + "public class A {} \n"); + + File jackOutputFile = AbstractTestTools.createTempDir(); + if (!jackOutputFile.setReadable(false)) { + Assert.fail("Fails to change file permissions of " + jackOutputFile.getAbsolutePath()); + } + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + + try { + jackApiToolchain.srcToLib( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + jackOutputFile, false, te.getSourceFolder()); + Assert.fail(); + } catch (PropertyIdException e) { + // Failure is ok since jack output folder is not readable + } finally { + if (!jackOutputFile.setReadable(true)) { + Assert.fail("Fails to change file permissions of " + jackOutputFile.getAbsolutePath()); + } + } + } + + /** + * Checks that compilation fails correctly when source file is not readable. + */ + @Test + public void testFileAccessError003() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + File a = AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", + "package jack.incremental; \n"+ + "public class A {} \n"); + if (!a.setReadable(false)) { + Assert.fail("Fails to change file permissions of " + a.getAbsolutePath()); + } + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + jackApiToolchain.setErrorStream(errOut); + + try { + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + te.getOutputDexFolder(), te.getSourceFolder()); + Assert.fail(); + } catch (FrontendCompilationException e) { + // Failure is ok since source file is not readable + } finally { + if (!a.setReadable(true)) { + Assert.fail("Fails to change file permissions of " + a.getAbsolutePath()); + } + Assert.assertTrue(errOut.toString().contains("Permission denied")); + } + } + + /** + * Checks that compilation fails correctly when jack file is not readable. + */ + @Test + public void testFileAccessError004() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + AbstractTestTools.createJavaFile(te.getSourceFolder(), "jack.incremental", "A.java", + "package jack.incremental; \n"+ + "public class A {} \n"); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + + jackApiToolchain.srcToLib( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + te.getJackFolder(), false, te.getSourceFolder()); + + AbstractTestTools.deleteJavaFile(te.getSourceFolder(), "jack.incremental", "A.java"); + + AbstractTestTools.createJavaFile(te.getSourceFolder(),"jack.incremental", "B.java", + "package jack.incremental; \n"+ + "public class B extends A {} \n"); + + ByteArrayOutputStream errOut = new ByteArrayOutputStream(); + try { + for (File jackFile : AbstractTestTools.getFiles(te.getJackFolder(), ".jack")) { + if (!jackFile.setReadable(false)) { + Assert.fail("Fails to change file permissions of " + jackFile.getAbsolutePath()); + } + } + + jackApiToolchain.setErrorStream(errOut); + jackApiToolchain.srcToLib( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()) + + File.pathSeparator + te.getJackFolder().getAbsolutePath(), + AbstractTestTools.createTempDir(), false, te.getSourceFolder()); + Assert.fail(); + } catch (JackLoadingException e) { + // Failure is ok since jack file is not readable + } finally { + Assert.assertEquals("", errOut.toString()); + for (File jackFile : AbstractTestTools.getFiles(te.getJackFolder(), ".jack")) { + if (!jackFile.setReadable(true)) { + Assert.fail("Fails to change file permissions of " + jackFile.getAbsolutePath()); + } + } + } + } + + /** + * Checks that compilation fails correctly when source file does not exist. + */ + @Test + public void testFileAccessError005() throws Exception { + ErrorTestHelper te = new ErrorTestHelper(); + + JackApiToolchain jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + + try { + + jackApiToolchain.srcToExe( + AbstractTestTools.getClasspathAsString(jackApiToolchain.getDefaultBootClasspath()), + te.getOutputDexFolder(), new File(te.getSourceFolder(), "A.java")); + + Assert.fail(); + } catch (JackUserException e) { + // Failure is ok since source file is not readable + Assert.assertTrue(e.getMessage().contains("A.java is missing")); + } + } +} diff --git a/jack-tests/tests/com/android/jack/experimenal/incremental/DependenciesTests005.java b/jack-tests/tests/com/android/jack/experimenal/incremental/DependenciesTests005.java new file mode 100644 index 0000000..6b50799 --- /dev/null +++ b/jack-tests/tests/com/android/jack/experimenal/incremental/DependenciesTests005.java @@ -0,0 +1,80 @@ +/* + * 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.experimenal.incremental; + +import com.android.jack.Main; +import com.android.jack.test.helper.IncrementalTestHelper; +import com.android.jack.test.toolchain.AbstractTestTools; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.List; + +/** + * JUnit test checking dependencies between Java files. + */ +public class DependenciesTests005 { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + /** + * Check that runtime is correct after incremental compilation due to a constant modification. + */ + @Test + public void testDependency001() throws Exception { + IncrementalTestHelper ite = + new IncrementalTestHelper(AbstractTestTools.createTempDir()); + + ite.addJavaFile("jack.incremental", "A.java", + "package jack.incremental; \n"+ + "public class A { public static void main(String[] args) {" + + "System.out.print(C.str + B.str);} " + + "} \n"); + + ite.addJavaFile("jack.incremental", "B.java", + "package jack.incremental; \n"+ + "public class B { public static final String str = \"HELLO\"; } \n"); + + ite.addJavaFile("jack.incremental", "C.java", + "package jack.incremental; \n"+ + "public class C { public static final String str = \"STRING:\"; } \n"); + + ite.incrementalBuildFromFolder(); + ite.snapshotJackFilesModificationDate(); + + ite.addJavaFile("jack.incremental", "B.java", + "package jack.incremental; \n"+ + "public class B { public static final String str = \"INCREMENTAL\"; } \n"); + + ite.incrementalBuildFromFolder(); + + List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes(); + Assert.assertEquals(2, fqnOfRebuiltTypes.size()); + Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A")); + Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B")); + + Assert.assertEquals("STRING:INCREMENTAL", ite.run("jack.incremental.A")); + } + + +} diff --git a/jack-tests/tests/com/android/jack/invoke/test001/jack/InvokeClone.java b/jack-tests/tests/com/android/jack/invoke/test001/jack/InvokeClone.java index 8d1296f..f06857a 100644 --- a/jack-tests/tests/com/android/jack/invoke/test001/jack/InvokeClone.java +++ b/jack-tests/tests/com/android/jack/invoke/test001/jack/InvokeClone.java @@ -19,7 +19,7 @@ package com.android.jack.invoke.test001.jack; public class InvokeClone { public static int[] getArray() { - int []a = new int[] {1,2,3}; + int[] a = new int[] {1,2,3}; return (a.clone()); } } diff --git a/jack-tests/tests/com/android/jack/jarjar/JarjarTests.java b/jack-tests/tests/com/android/jack/jarjar/JarjarTests.java new file mode 100644 index 0000000..6bac5cc --- /dev/null +++ b/jack-tests/tests/com/android/jack/jarjar/JarjarTests.java @@ -0,0 +1,48 @@ +/* + * 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.jarjar; + +import com.android.jack.test.helper.RuntimeTestHelper; +import com.android.jack.test.runtime.RuntimeTestInfo; +import com.android.jack.test.toolchain.AbstractTestTools; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import javax.annotation.Nonnull; + +public class JarjarTests { + + @Nonnull + private File baseDir; + + @Before + public void setUp() { + baseDir = AbstractTestTools.getTestRootDir("com.android.jack.jarjar.test001"); + } + + @Test + public void compileAndRunTest() throws Exception { + RuntimeTestHelper rtEnv = new RuntimeTestHelper( + new RuntimeTestInfo(baseDir, "com.android.jack.jarjar.test001.dx.Tests")); + rtEnv.setJarjarRulesFileName("jarjar-rules.txt"); + rtEnv.compileAndRunTest(); + } + +} diff --git a/jack-tests/tests/com/android/jack/java7/ExceptionsTest.java b/jack-tests/tests/com/android/jack/java7/ExceptionsTest.java new file mode 100644 index 0000000..ecbe578 --- /dev/null +++ b/jack-tests/tests/com/android/jack/java7/ExceptionsTest.java @@ -0,0 +1,74 @@ +/* + * 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.java7; + +import com.android.jack.Main; +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.JackBasedToolchain; +import com.android.jack.test.toolchain.Toolchain.SourceLevel; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; + +import javax.annotation.Nonnull; + +/** + * JUnit test for compilation of Java 7 features + */ +public class ExceptionsTest { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void java7Exception001() throws Exception { + compileJava7Test("test001"); + } + + @Test + public void java7Exception002() throws Exception { + compileJava7Test("test002"); + } + + @Test + public void java7Exception003() throws Exception { + compileJava7Test("test003"); + } + + @Test + public void java7Exception004() throws Exception { + compileJava7Test("test004"); + } + + @Test + public void java7Exception005() throws Exception { + compileJava7Test("test005"); + } + + private void compileJava7Test(@Nonnull String name) throws Exception { + JackBasedToolchain jackBasedToolchain = + AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + jackBasedToolchain.setSourceLevel(SourceLevel.JAVA_7).srcToExe( + AbstractTestTools.getClasspathAsString(jackBasedToolchain.getDefaultBootClasspath()), + AbstractTestTools.createTempDir(), new File( + AbstractTestTools.getTestRootDir("com.android.jack.java7.exceptions." + name), "jack")); + } +} diff --git a/jack-tests/tests/com/android/jack/shrob/AbstractTest.java b/jack-tests/tests/com/android/jack/shrob/AbstractTest.java new file mode 100644 index 0000000..319abaf --- /dev/null +++ b/jack-tests/tests/com/android/jack/shrob/AbstractTest.java @@ -0,0 +1,561 @@ +/* + * 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.shrob; + +import com.android.jack.Main; +import com.android.jack.ProguardFlags; +import com.android.jack.TestTools; +import com.android.jack.category.KnownBugs; +import com.android.jack.category.SlowTests; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import javax.annotation.Nonnull; + +/** + * Abstract class for running shrob tests + */ +public abstract class AbstractTest { + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + protected abstract void runTest( + @Nonnull String testNumber, + @Nonnull String flagNumber, + @Nonnull String mappingNumber) + throws Exception; + + protected ProguardFlags generateApplyMapping(@Nonnull File mappingFile) throws IOException { + File applyMapping = TestTools.createTempFile("mapping.flags", null); + BufferedWriter writer = new BufferedWriter(new FileWriter(applyMapping)); + writer.append("-applymapping "); + writer.append(mappingFile.getAbsolutePath()); + writer.close(); + return new ProguardFlags(applyMapping); + } + + protected ProguardFlags generateInjars(@Nonnull File injar) throws IOException { + File injarFlags = TestTools.createTempFile("injars", ".flags"); + BufferedWriter writer = new BufferedWriter(new FileWriter(injarFlags)); + writer.append("-injars "); + writer.append(injar.getAbsolutePath()); + writer.close(); + return new ProguardFlags(injarFlags); + } + + @Test + public void test1_001() throws Exception { + runTest("001", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_002() throws Exception { + runTest("001", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_003() throws Exception { + runTest("001", "003", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_004() throws Exception { + runTest("001", "004", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_005() throws Exception { + runTest("001", "005", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_005_002() throws Exception { + runTest("001", "005", "002"); + } + + @Test + @Category(SlowTests.class) + public void test1_005_003() throws Exception { + runTest("001", "005", "003"); + } + + @Test + @Category(SlowTests.class) + public void test1_006() throws Exception { + runTest("001", "006", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_007() throws Exception { + runTest("001", "007", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_008() throws Exception { + runTest("001", "008", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_009() throws Exception { + runTest("001", "009", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_010() throws Exception { + runTest("001", "010", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_011() throws Exception { + runTest("001", "011", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_012() throws Exception { + runTest("001", "012", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_013() throws Exception { + runTest("001", "013", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_014() throws Exception { + runTest("001", "014", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_015() throws Exception { + runTest("001", "015", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_016() throws Exception { + runTest("001", "016", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_017() throws Exception { + runTest("001", "017", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_018() throws Exception { + runTest("001", "018", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_019() throws Exception { + runTest("001", "019", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_020() throws Exception { + runTest("001", "020", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_021() throws Exception { + runTest("001", "021", ""); + } + + @Test + @Category(SlowTests.class) + public void test1_022() throws Exception { + runTest("001", "022", ""); + } + + @Test + public void test2_001() throws Exception { + runTest("002", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test2_002() throws Exception { + runTest("002", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test2_003() throws Exception { + runTest("002", "003", ""); + } + + @Test + public void test4_001() throws Exception { + runTest("004", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test4_002() throws Exception { + runTest("004", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test4_003() throws Exception { + runTest("004", "003", ""); + } + + @Test + public void test5_001() throws Exception { + runTest("005", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_002() throws Exception { + runTest("005", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_003() throws Exception { + runTest("005", "003", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_004() throws Exception { + runTest("005", "004", ""); + } + + @Test + public void test5_005() throws Exception { + runTest("005", "005", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_006() throws Exception { + runTest("005", "006", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_007() throws Exception { + runTest("005", "007", ""); + } + + @Test + @Category(SlowTests.class) + public void test5_008() throws Exception { + runTest("005", "008", ""); + } + + @Test + public void test6_001() throws Exception { + runTest("006", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test6_002() throws Exception { + runTest("006", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test6_003() throws Exception { + runTest("006", "003", ""); + } + + @Test + @Category(SlowTests.class) + public void test6_004() throws Exception { + runTest("006", "004", ""); + } + + @Test + @Category(SlowTests.class) + public void test6_005() throws Exception { + runTest("006", "005", ""); + } + + @Test + @Category(SlowTests.class) + public void test6_006() throws Exception { + runTest("006", "006", ""); + } + + @Test + public void test7_001() throws Exception { + runTest("007", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test7_002() throws Exception { + runTest("007", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test7_003() throws Exception { + runTest("007", "003", ""); + } + + @Test + public void test8_001() throws Exception { + runTest("008", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test8_002() throws Exception { + runTest("008", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test8_003() throws Exception { + runTest("008", "003", ""); + } + + @Test + @Category(SlowTests.class) + public void test8_004() throws Exception { + runTest("008", "004", ""); + } + + @Test + public void test9_001() throws Exception { + runTest("009", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test9_002() throws Exception { + runTest("009", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test9_003() throws Exception { + runTest("009", "003", ""); + } + + @Test + public void test10_001() throws Exception { + runTest("010", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test10_002() throws Exception { + runTest("010", "002", ""); + } + + @Test + @Category(SlowTests.class) + public void test10_003() throws Exception { + runTest("010", "003", ""); + } + + @Test + public void test11_001() throws Exception { + runTest("011", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test11_002() throws Exception { + runTest("011", "002", ""); + } + + @Test + public void test12_001() throws Exception { + runTest("012", "001", ""); + } + + @Test + public void test13_001() throws Exception { + runTest("013", "001", ""); + } + + @Test + public void test14_001() throws Exception { + runTest("014", "001", ""); + } + + @Test + public void test15_001() throws Exception { + runTest("015", "001", ""); + } + + @Test + public void test16_001() throws Exception { + runTest("016", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test16_002() throws Exception { + runTest("016", "002", ""); + } + + @Test + public void test17_001() throws Exception { + runTest("017", "001", ""); + } + + @Test + public void test18_001() throws Exception { + runTest("018", "001", ""); + } + + @Test + public void test19_001() throws Exception { + runTest("019", "001", ""); + } + + @Test + public void test21_001() throws Exception { + runTest("021", "001", ""); + } + + @Test + public void test22_001() throws Exception { + runTest("022", "001", ""); + } + + @Test + public void test23_001() throws Exception { + runTest("023", "001", ""); + } + + @Test + public void test25_001() throws Exception { + runTest("025", "001", ""); + } + + @Test + public void test26_001() throws Exception { + runTest("026", "001", ""); + } + + @Test + public void test29_001() throws Exception { + runTest("029", "001", ""); + } + + @Test + public void test30_001() throws Exception { + runTest("030", "001", ""); + } + + @Test + public void test31_001() throws Exception { + runTest("031", "001", ""); + } + + @Test + public void test31_002() throws Exception { + runTest("031", "002", ""); + } + + @Test + public void test32_001() throws Exception { + runTest("032", "001", ""); + } + + @Test + public void test33_001() throws Exception { + runTest("033", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test33_002() throws Exception { + runTest("033", "002", ""); + } + + @Test + public void test34_001() throws Exception { + runTest("034", "001", ""); + } + + @Test + public void test35_001() throws Exception { + runTest("035", "001", ""); + } + + @Test + public void test36_001() throws Exception { + runTest("036", "001", ""); + } + + @Test + public void test37_001() throws Exception { + runTest("037", "001", ""); + } + + @Test + public void test38_001() throws Exception { + runTest("038", "001", ""); + } + + @Test + public void test39_001() throws Exception { + runTest("039", "001", ""); + } + + @Test + @Category(SlowTests.class) + public void test40_001() throws Exception { + runTest("040", "001", ""); + } + + @Test + @Category(KnownBugs.class) + public void test41_001() throws Exception { + runTest("041", "001", ""); + } +} diff --git a/jack-tests/tests/com/android/jack/shrob/ObfuscationWithoutMappingTests.java b/jack-tests/tests/com/android/jack/shrob/ObfuscationWithoutMappingTests.java new file mode 100644 index 0000000..26cba49 --- /dev/null +++ b/jack-tests/tests/com/android/jack/shrob/ObfuscationWithoutMappingTests.java @@ -0,0 +1,166 @@ +/* + * 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.shrob; + +import com.android.jack.Options; +import com.android.jack.category.KnownBugs; +import com.android.jack.shrob.obfuscation.NameProviderFactory; +import com.android.jack.shrob.proguard.GrammarActions; +import com.android.jack.shrob.spec.Flags; +import com.android.jack.test.comparator.ComparatorMapping; +import com.android.jack.test.helper.SourceToDexComparisonTestHelper; +import com.android.jack.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.DummyToolchain; +import com.android.jack.test.toolchain.JackApiToolchain; + +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.File; + +import javax.annotation.Nonnull; + +public class ObfuscationWithoutMappingTests extends AbstractTest { + + @Override + protected void runTest( + @Nonnull String testNumber, + @Nonnull String flagNumber, + @Nonnull String mappingNumber) + throws Exception { + + String testName = "shrob/test" + testNumber; + + String testPackageName = "com.android.jack.shrob.test" + testNumber; + File testFolder = AbstractTestTools.getTestRootDir(testPackageName); + + JackApiToolchain toolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchain.class); + Flags flags = new Flags(); + toolchain.setShrobFlags(flags); + GrammarActions.parse("proguard.flags" + flagNumber, testFolder.getAbsolutePath(), flags); + File refFolder = new File(testFolder, "refsObfuscationWithoutMapping"); + + toolchain.addProperty(NameProviderFactory.NAMEPROVIDER.getName(), "rot13"); + toolchain.addProperty(Options.METHOD_FILTER.getName(), "supported-methods"); + + File candidateOutputMapping = AbstractTestTools.createTempFile("mapping", ".txt"); + File refOutputMapping = new File(refFolder, "expected-" + flagNumber + ".txt"); + flags.setOutputMapping(candidateOutputMapping); + flags.setPrintMapping(true); + + SourceToDexComparisonTestHelper env = + new SourceToDexComparisonTestHelper(new File(testFolder, "jack")); + + env.setCandidateTestTools(toolchain); + env.setReferenceTestTools(new DummyToolchain()); + + env.runTest(new ComparatorMapping(candidateOutputMapping, refOutputMapping)); + + // // ============================================================== + + /* + * Comment for reviewers: the following code is an attempt to use jack based toolchain. + * It works, but requires to manipulate (copy, write...) shrob flags. + * Here I added a shrob option manually, but ideally to be able to use + * a Flags like object for CLI based toochains, we must be able to dump + * the flags as a file (TBD). Plus, if flags have "-include" directives with relative + * paths, files must be in the same location. + */ + +// +// String testName = "shrob/test" + testNumber; +// File testFolder = TestTools.getJackTestFolder(testName); +// File refFolder = new File(testFolder, "refsObfuscationWithoutMapping"); +// +// JackBasedToolchain jackToolchain = AbstractTestTools.getJackBasedToolchainAsCandidate(); +// +// jackToolchain.addProperty(NameProviderFactory.NAMEPROVIDER.getName(), "rot13"); +// jackToolchain.addProperty(Options.METHOD_FILTER.getName(), "supported-methods"); +// +// File candidateOutputMapping = TestTools.createTempFile("mapping", ".txt"); +// File refOutputMapping = new File(refFolder, "expected-" + flagNumber + ".txt"); +// +// jackToolchain.getCompilationResult().proguardMappingFile = candidateOutputMapping; +// +// // TODO(jmhenaff): having to seems like a no go for JackBasedToolchain (i.e. cli) +// File candidateFlags = new File(testFolder.getAbsolutePath(), "tmp-proguard.flags" + flagNumber); +// candidateFlags.deleteOnExit(); +// appendStringToFileCopy(new File(testFolder.getAbsolutePath(), "proguard.flags" + flagNumber), +// candidateFlags, "-printmapping " + candidateOutputMapping.getAbsolutePath()); +// +// SourceToDexComparisonTestEnv env = new SourceToDexComparisonTestEnv(bootclasspath, classpath, +// TestTools.getJackTestsWithJackFolder(testName)); +// +// env.setProguardFlags(new ProguardFlags[] {new ProguardFlags(candidateFlags)}); +// +// CompilationResult compilationResult = new CompilationResult(); +// compilationResult.proguardMappingFile = refOutputMapping; +// +// env.setCandidateTestTools(jackToolchain); +// env.setReferenceTestTools(new DummyTestTools(compilationResult)); +// +// env.addComparator(env.createMappingComparator()); +// env.compare(); + + } + + @Override + @Test + @Category(KnownBugs.class) + public void test33_001() throws Exception { + super.test33_001(); + } + + @Override + @Test + @Category(KnownBugs.class) + public void test34_001() throws Exception { + super.test34_001(); + } + + @Override + @Test + @Category(KnownBugs.class) + public void test35_001() throws Exception { + super.test35_001(); + } + +// private void appendStringToFileCopy(File source, File dest, String appended) throws IOException { +// BufferedReader reader = null; +// BufferedWriter writer = null; +// try { +// reader = new BufferedReader(new FileReader(source)); +// writer = new BufferedWriter(new FileWriter(dest)); +// String line; +// while ((line = reader.readLine()) != null) { +// writer.write(line); +// writer.write("\n"); +// } +// writer.write(appended); +// } finally { +// try { +// reader.close(); +// } catch (IOException e) { +// } +// try { +// writer.close(); +// } catch (IOException e) { +// } +// } +// } + +} diff --git a/jack-tests/tests/com/android/jack/tools/merger/MergerAllTests.java b/jack-tests/tests/com/android/jack/tools/merger/MergerAllTests.java new file mode 100644 index 0000000..6d222de --- /dev/null +++ b/jack-tests/tests/com/android/jack/tools/merger/MergerAllTests.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.tools.merger; + +import com.android.jack.tools.merger.test011.MergerTest011; + +import org.junit.experimental.categories.Categories; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Categories.class) +@SuiteClasses( +value = {MergerTest011.class}) +public class MergerAllTests { +}
\ No newline at end of file diff --git a/jack-tests/tests/com/android/jack/tools/merger/MergerTestTools.java b/jack-tests/tests/com/android/jack/tools/merger/MergerTestTools.java new file mode 100644 index 0000000..dab9e7e --- /dev/null +++ b/jack-tests/tests/com/android/jack/tools/merger/MergerTestTools.java @@ -0,0 +1,59 @@ +/* + * 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.tools.merger; + +import com.android.jack.Options; +import com.android.jack.TestTools; +import com.android.jack.backend.dex.DexFileWriter; +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.CheckForNull; +import javax.annotation.Nonnull; + +public class MergerTestTools { + + @Nonnull + protected File buildOneDexPerType(@CheckForNull String classpath, @Nonnull File sourceFolder, + boolean withDebug) throws Exception { + JackBasedToolchain toolchain = + AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + try { + File multiDexFolder = TestTools.createTempDir("multi", "dex"); + File multiDex = new File(multiDexFolder, DexFileWriter.DEX_FILENAME); + File multiDexOnTypePerTypeFolder = TestTools.createTempDir("multiOnDexPerType", "dex"); + + toolchain.addProperty(Options.EMIT_LINE_NUMBER_DEBUG_INFO.getName(), + Boolean.toString(withDebug)); + toolchain.addProperty(ScheduleInstance.DEFAULT_RUNNER.getName(), "single-threaded"); + toolchain.addProperty(Options.TYPEDEX_DIR.getName(), + multiDexOnTypePerTypeFolder.getAbsolutePath()); + + toolchain.srcToExe(classpath, multiDexFolder, sourceFolder); + + return multiDex; + + } catch (IOException e) { + throw new AssertionError(e); + } + } + +} diff --git a/jack-tests/tests/com/android/jack/tools/merger/test011/MergerTest011.java b/jack-tests/tests/com/android/jack/tools/merger/test011/MergerTest011.java new file mode 100644 index 0000000..f00da22 --- /dev/null +++ b/jack-tests/tests/com/android/jack/tools/merger/test011/MergerTest011.java @@ -0,0 +1,185 @@ +/* + * 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.tools.merger.test011; + +import com.android.jack.JackUserException; +import com.android.jack.Main; +import com.android.jack.TestTools; +import com.android.jack.category.SlowTests; +import com.android.jack.tools.merger.FieldIdOverflowException; +import com.android.jack.tools.merger.MergerTestTools; +import com.android.jack.tools.merger.MethodIdOverflowException; +import com.android.jack.tools.merger.TypeIdOverflowException; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +/** + * JUnit test checking that merging can throw overflow exceptions. + */ +public class MergerTest011 extends MergerTestTools { + + private static int fileCount = 655; + + private static final String expectedExceptionMessage = + "Index overflow while merging dex files. Try using multidex"; + + @BeforeClass + public static void setUpClass() { + Main.class.getClassLoader().setDefaultAssertionStatus(true); + } + + @Test + public void testMergerWithHighNumberOfMethods() throws Exception { + File srcFolder = TestTools.createTempDir("oneDexPerType", "SrcFolder"); + + // One CstMethodRef is also created for call to object.init() + for (int fileIdx = 0; fileIdx < fileCount; fileIdx++) { + generateJavaFileWithMethods(srcFolder, fileIdx, 100); + } + generateJavaFileWithMethods(srcFolder, fileCount, 36); + + try { + buildOneDexPerType(TestTools.getDefaultBootclasspathString(), srcFolder, false /* withDebug */); + Assert.fail(); + } catch (JackUserException e) { + Assert.assertEquals(expectedExceptionMessage, e.getMessage()); + Throwable cause = e.getCause(); + Assert.assertTrue(cause instanceof MethodIdOverflowException); + } + } + + @Test + public void testMergerWithHighNumberOfFields() throws Exception { + File srcFolder = TestTools.createTempDir("oneDexPerType", "SrcFolder"); + + for (int fileIdx = 0; fileIdx < fileCount; fileIdx++) { + generateJavaFileWithFields(srcFolder, fileIdx, 100); + } + generateJavaFileWithFields(srcFolder, fileCount, 37); + + try { + buildOneDexPerType(TestTools.getDefaultBootclasspathString(), srcFolder, false /* withDebug */); + Assert.fail(); + } catch (JackUserException e) { + Assert.assertEquals(expectedExceptionMessage, e.getMessage()); + Throwable cause = e.getCause(); + Assert.assertTrue(cause instanceof FieldIdOverflowException); + } + } + + @Test + @Category(SlowTests.class) + public void testMergerWithHighNumberOfTypes() throws Exception { + File srcFolder = TestTools.createTempDir("oneDexPerType", "SrcFolder"); + + for (int fileIdx = 0; fileIdx < fileCount; fileIdx++) { + generateJavaFileWithTypes(srcFolder, fileIdx, 100); + } + generateJavaFileWithTypes(srcFolder, fileCount, 36); + + try { + buildOneDexPerType(TestTools.getDefaultBootclasspathString(), srcFolder, false /* withDebug */); + Assert.fail(); + } catch (JackUserException e) { + Assert.assertEquals(expectedExceptionMessage, e.getMessage()); + Throwable cause = e.getCause(); + Assert.assertTrue(cause instanceof TypeIdOverflowException); + } + } + + private void generateJavaFileWithMethods(@Nonnull File srcFolder, @Nonnegative int fileIdx, + @Nonnegative int methodCount) throws IOException, FileNotFoundException { + File javaFile = new File(srcFolder, "A" + fileIdx + ".java"); + if (!javaFile.createNewFile()) { + throw new IOException("Failed to create file " + javaFile.getAbsolutePath()); + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(javaFile); + StringBuilder content = + new StringBuilder("package jack.merger; \n" + "public class A" + fileIdx+ " {"); + // -1 due to implicit init method + for (int mthIdx = 0; mthIdx < methodCount - 1; mthIdx++) { + content.append("public void m" + mthIdx + "() {}"); + } + content.append("} \n"); + fos.write(content.toString().getBytes()); + } finally { + if (fos != null) { + fos.close(); + } + } + } + + private void generateJavaFileWithFields(@Nonnull File srcFolder, @Nonnegative int fileIdx, + @Nonnegative int fieldCount) throws IOException, FileNotFoundException { + File javaFile = new File(srcFolder, "A" + fileIdx + ".java"); + if (!javaFile.createNewFile()) { + throw new IOException("Failed to create file " + javaFile.getAbsolutePath()); + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(javaFile); + StringBuilder content = + new StringBuilder("package jack.merger; \n" + "public class A" + fileIdx+ " {"); + for (int fieldIdx = 0; fieldIdx < fieldCount; fieldIdx++) { + content.append("public int f" + fieldIdx + ";"); + } + content.append("} \n"); + fos.write(content.toString().getBytes()); + } finally { + if (fos != null) { + fos.close(); + } + } + } + + private void generateJavaFileWithTypes(@Nonnull File srcFolder, @Nonnegative int fileIdx, + @Nonnegative int typeCount) throws IOException, FileNotFoundException { + File javaFile = new File(srcFolder, "A" + fileIdx + ".java"); + if (!javaFile.createNewFile()) { + throw new IOException("Failed to create file " + javaFile.getAbsolutePath()); + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(javaFile); + StringBuilder content = + new StringBuilder("package jack.merger; \n" + "public class A" + fileIdx+ " {"); + for (int typeIdx = 0; typeIdx < typeCount; typeIdx++) { + content.append("public class c" + typeIdx + " {}"); + } + content.append("} \n"); + fos.write(content.toString().getBytes()); + } finally { + if (fos != null) { + fos.close(); + } + } + } +} diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java index 4eebae8..f6166eb 100644 --- a/jack/src/com/android/jack/Options.java +++ b/jack/src/com/android/jack/Options.java @@ -699,6 +699,15 @@ public class Options { return outZip != null || jayceOutZip != null; } + @CheckForNull + public Flags getFlags() { + return flags; + } + + public void setFlags(@Nonnull Flags flags) { + this.flags = flags; + } + public void applyShrobFlags() { assert flags != null; List<File> inJars = flags.getInJars(); @@ -761,6 +770,15 @@ public class Options { this.proguardFlagsFiles = proguardFlagsFiles; } + @CheckForNull + public File getJarjarRulesFile() { + return jarjarRulesFile; + } + + public void setJarjarRulesFile(@Nonnull File jarjarRulesFile) { + this.jarjarRulesFile = jarjarRulesFile; + } + public void setNameProvider(@Nonnull String nameProvider) { properties.put(NameProviderFactory.NAMEPROVIDER.getName(), nameProvider); } diff --git a/jack/tests/com/android/jack/AllTestsWithoutKnownBugs.java b/jack/tests/com/android/jack/AllTestsWithoutKnownBugs.java new file mode 100644 index 0000000..683e265 --- /dev/null +++ b/jack/tests/com/android/jack/AllTestsWithoutKnownBugs.java @@ -0,0 +1,34 @@ +/* + * 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; + +import com.android.jack.category.KnownBugs; + +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Categories.ExcludeCategory; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Test suite containing all tests (except for regression tests that must be run from the command + * line). + */ +@RunWith(Categories.class) +@ExcludeCategory(KnownBugs.class) +@SuiteClasses(value = {AllTests.class}) +public class AllTestsWithoutKnownBugs { +} diff --git a/junit4/Android.mk b/junit4/Android.mk index aeb9366..321f2fa 100644 --- a/junit4/Android.mk +++ b/junit4/Android.mk @@ -42,3 +42,20 @@ LOCAL_MODULE_TAGS := optional LOCAL_BUILD_HOST_DEX := true include $(BUILD_HOST_JAVA_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java) + +LOCAL_JAVA_LIBRARIES := core + +LOCAL_JAVA_LIBRARIES := hamcrest-core-target-jack + +LOCAL_STATIC_JAVA_LIBRARIES := hamcrest-core-target-jack + +LOCAL_MODULE := junit4-targetdex-jack + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_JAVA_LIBRARY) |