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