blob: 912bdbd07e52201a7b488830307f61c524baab95 [file] [log] [blame]
/*
* 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.build.gradle.integration.common.fixture;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.integration.common.fixture.app.AbstractAndroidTestApp;
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp;
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile;
import com.android.build.gradle.integration.common.utils.FileHelper;
import com.android.build.gradle.integration.common.utils.JacocoAgent;
import com.android.build.gradle.integration.common.utils.SdkHelper;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.SyncIssue;
import com.android.builder.Version;
import com.android.io.StreamException;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import org.gradle.tooling.BuildAction;
import org.gradle.tooling.BuildActionExecuter;
import org.gradle.tooling.BuildLauncher;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
import org.gradle.tooling.model.GradleProject;
import org.gradle.tooling.model.GradleTask;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* JUnit4 test rule for integration test.
*
* This rule create a gradle project in a temporary directory.
* It can be use with the @Rule or @ClassRule annotations. Using this class with @Rule will create
* a gradle project in separate directories for each unit test, whereas using it with @ClassRule
* creates a single gradle project.
*
* The test directory is always deleted if it already exists at the start of the test to ensure a
* clean environment.
*/
public class GradleTestProject implements TestRule {
public static final int DEFAULT_COMPILE_SDK_VERSION = 21;
public static final String DEFAULT_BUILD_TOOL_VERSION;
public static final String GRADLE_TEST_VERSION = "2.2.1";
public static final String GRADLE_EXP_TEST_VERSION = "2.4-20150322230018+0000";
public static final String ANDROID_GRADLE_PLUGIN_VERSION;
public static final String CUSTOM_JACK;
static {
String envBuildToolVersion = System.getenv("CUSTOM_BUILDTOOLS");
DEFAULT_BUILD_TOOL_VERSION = !Strings.isNullOrEmpty(envBuildToolVersion) ?
envBuildToolVersion : "22.0.1";
String envVersion = System.getenv().get("CUSTOM_GRADLE");
ANDROID_GRADLE_PLUGIN_VERSION = !Strings.isNullOrEmpty(envVersion) ? envVersion
: Version.ANDROID_GRADLE_PLUGIN_VERSION;
String envJack = System.getenv().get("CUSTOM_JACK");
CUSTOM_JACK = !Strings.isNullOrEmpty(envJack) ? envJack : "false";
}
private static final String COMMON_HEADER = "commonHeader.gradle";
private static final String COMMON_LOCAL_REPO = "commonLocalRepo.gradle";
private static final String COMMON_BUILD_SCRIPT = "commonBuildScript.gradle";
private static final String COMMON_BUILD_SCRIPT_EXP = "commonBuildScriptExperimental.gradle";
private static final String COMMON_GRADLE_PLUGIN_VERSION = "commonGradlePluginVersion.gradle";
private static final String DEFAULT_TEST_PROJECT_NAME = "project";
public static class Builder {
private static final File SAMPLE_PROJECT_DIR = new File("samples");
private static final File TEST_PROJECT_DIR = new File("test-projects");
@Nullable
private String name;
@Nullable
private TestProject testProject = null;
boolean captureStdOut = false;
boolean captureStdErr = false;
boolean experimentalMode = false;
@Nullable
private String heapSize;
/**
* Create a GradleTestProject.
*/
public GradleTestProject create() {
return new GradleTestProject(
name,
testProject,
experimentalMode,
experimentalMode ? GRADLE_EXP_TEST_VERSION : GRADLE_TEST_VERSION,
captureStdOut,
captureStdErr,
heapSize);
}
/**
* Set the name of the project.
*
* Necessary if you have multiple projects in a test class.
*/
public Builder withName(@NonNull String name) {
this.name = name;
return this;
}
public Builder captureStdOut(boolean captureStdOut) {
this.captureStdOut = captureStdOut;
return this;
}
public Builder captureStdErr(boolean captureStdErr) {
this.captureStdErr = captureStdErr;
return this;
}
public Builder forExpermimentalPlugin(boolean mode) {
this.experimentalMode = mode;
return this;
}
/**
* Create GradleTestProject from a TestProject.
*/
public Builder fromTestApp(@NonNull TestProject testProject) {
this.testProject = testProject;
return this;
}
/**
* Create GradleTestProject from an existing test project.
*/
public Builder fromTestProject(@NonNull String project) {
AndroidTestApp app = new EmptyTestApp();
name = project;
File projectDir = new File(TEST_PROJECT_DIR, project);
addAllFiles(app, projectDir);
return fromTestApp(app);
}
/**
* Sets the test heap size requirement. Example values : 1024m, 2048m...
*
* @param heapSize the heap size in a format understood by the -Xmx JVM parameter
* @return itself.
*/
public Builder withHeap(String heapSize) {
this.heapSize = heapSize;
return this;
}
private static class EmptyTestApp extends AbstractAndroidTestApp {
@Override
public boolean containsFullBuildScript() {
return true;
}
}
}
private String name;
private File outDir;
private File testDir;
private File sourceDir;
private File buildFile;
private File ndkDir;
private File sdkDir;
private final ByteArrayOutputStream stdout;
private final ByteArrayOutputStream stderr;
@Nullable
private TestProject testProject;
private boolean experimentalMode;
private String targetGradleVersion;
@Nullable
private String heapSize;
private GradleTestProject(
@Nullable String name,
@Nullable TestProject testProject,
boolean experimentalMode,
String targetGradleVersion,
boolean captureStdOut,
boolean captureStdErr,
@Nullable String heapSize) {
sdkDir = SdkHelper.findSdkDir();
ndkDir = findNdkDir();
String buildDir = System.getenv("PROJECT_BUILD_DIR");
outDir = (buildDir == null) ? new File("build/tests") : new File(buildDir, "tests");
this.name = (name == null) ? DEFAULT_TEST_PROJECT_NAME : name;
this.experimentalMode = experimentalMode;
this.targetGradleVersion = targetGradleVersion;
this.testProject = testProject;
stdout = captureStdOut ? new ByteArrayOutputStream() : null;
stderr = captureStdErr ? new ByteArrayOutputStream() : null;
this.heapSize = heapSize;
}
/**
* Create a GradleTestProject representing a subProject of another GradleTestProject.
* @param subProject name of the subProject.
* @param rootProject root GradleTestProject.
*/
private GradleTestProject(
@NonNull String subProject,
@NonNull GradleTestProject rootProject) {
name = subProject;
outDir = rootProject.outDir;
testDir = new File(rootProject.testDir, subProject);
assertTrue("No subproject dir at " + testDir.toString(), testDir.isDirectory());
buildFile = new File(testDir, "build.gradle");
sourceDir = new File(testDir, "src");
ndkDir = rootProject.ndkDir;
sdkDir = rootProject.sdkDir;
stdout = rootProject.stdout;
stderr = rootProject.stdout;
testProject = null;
}
public static Builder builder() {
return new Builder();
}
/**
* Recursively delete directory or file.
*
* @param root directory to delete
*/
private static void deleteRecursive(File root) {
if (root.exists()) {
if (root.isDirectory()) {
File files[] = root.listFiles();
if (files != null) {
for (File file : files) {
deleteRecursive(file);
}
}
}
assertTrue(root.delete());
}
}
/**
* Add all files in a directory to an AndroidTestApp.
*/
private static void addAllFiles(AndroidTestApp app, File projectDir) {
for (String filePath : FileHelper.listFiles(projectDir)) {
File file = new File(filePath);
try {
app.addFile(
new TestSourceFile(
file.getParent(),
file.getName(),
Files.toByteArray(new File(projectDir, filePath))));
} catch (IOException e) {
fail(e.toString());
}
}
}
@Override
public Statement apply(final Statement base, Description description) {
testDir = new File(outDir, description.getTestClass().getName());
// Create separate directory based on test method name if @Rule is used.
// getMethodName() is null if this rule is used as a @ClassRule.
if (description.getMethodName() != null) {
String dirName = description.getMethodName();
dirName = dirName.replaceAll("[^a-zA-Z0-9_]", "_");
testDir = new File(testDir, dirName);
}
testDir = new File(testDir, name);
buildFile = new File(testDir, "build.gradle");
sourceDir = new File(testDir, "src");
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (testDir.exists()) {
deleteRecursive(testDir);
}
assertTrue(testDir.mkdirs());
assertTrue(sourceDir.mkdirs());
Files.copy(
new File(Builder.TEST_PROJECT_DIR, COMMON_HEADER),
new File(testDir.getParent(), COMMON_HEADER));
Files.copy(
new File(Builder.TEST_PROJECT_DIR, COMMON_LOCAL_REPO),
new File(testDir.getParent(), COMMON_LOCAL_REPO));
Files.copy(
new File(Builder.TEST_PROJECT_DIR, COMMON_BUILD_SCRIPT),
new File(testDir.getParent(), COMMON_BUILD_SCRIPT));
Files.copy(
new File(Builder.TEST_PROJECT_DIR, COMMON_BUILD_SCRIPT_EXP),
new File(testDir.getParent(), COMMON_BUILD_SCRIPT_EXP));
Files.copy(
new File(Builder.TEST_PROJECT_DIR, COMMON_GRADLE_PLUGIN_VERSION),
new File(testDir.getParent(), COMMON_GRADLE_PLUGIN_VERSION));
if (testProject != null) {
testProject.write(
testDir,
testProject.containsFullBuildScript() ? "" :getGradleBuildscript());
} else {
Files.write(
getGradleBuildscript(),
buildFile,
Charsets.UTF_8);
}
createLocalProp(testDir, sdkDir, ndkDir);
base.evaluate();
}
};
}
/**
* Create a GradleTestProject representing a subproject.
*/
public GradleTestProject getSubproject(String name) {
return new GradleTestProject(name, this);
}
/**
* Return the name of the test project.
*/
public String getName() {
return name;
}
/**
* Return the directory containing the test project.
*/
public File getTestDir() {
return testDir;
}
/**
* Return the build.gradle of the test project.
*/
public File getBuildFile() {
return buildFile;
}
/**
* Return the directory containing the source files of the test project.
*/
public File getSourceDir() {
return sourceDir;
}
/**
* Return the output directory from Android plugins.
*/
public File getOutputDir() {
return new File(testDir,
Joiner.on(File.separator).join("build", AndroidProject.FD_OUTPUTS));
}
/**
* Return a File under the output directory from Android plugins.
*/
public File getOutputFile(String path) {
return new File(getOutputDir(), path);
}
/**
* Return the output apk File from the application plugin for the given dimension.
*
* Expected dimensions orders are:
* - product flavors
* - build type
* - other modifiers (e.g. "unsigned", "aligned")
*/
public File getApk(String ... dimensions) {
// TODO: Add overload for tests and splits.
List<String> dimensionList = Lists.newArrayListWithExpectedSize(1 + dimensions.length);
dimensionList.add(getName());
dimensionList.addAll(Arrays.asList(dimensions));
return getOutputFile(
"apk/" + Joiner.on("-").join(dimensionList) + SdkConstants.DOT_ANDROID_PACKAGE);
}
/**
* Return the output aar File from the library plugin for the given dimension.
*
* Expected dimensions orders are:
* - product flavors
* - build type
* - other modifiers (e.g. "unsigned", "aligned")
*/
public File getAar(String ... dimensions) {
// TODO: Add overload for tests and splits.
List<String> dimensionList = Lists.newArrayListWithExpectedSize(1 + dimensions.length);
dimensionList.add(getName());
dimensionList.addAll(Arrays.asList(dimensions));
return getOutputFile("aar/" + Joiner.on("-").join(dimensionList) + SdkConstants.DOT_AAR);
}
/**
* Returns the SDK dir
*/
public File getSdkDir() {
return sdkDir;
}
/**
* Returns the NDK dir
*/
public File getNdkDir() {
return ndkDir;
}
/**
* Return the directory of the repository containing the necessary plugins for testing.
*/
private File getRepoDir() {
CodeSource source = getClass().getProtectionDomain().getCodeSource();
assert (source != null);
URL location = source.getLocation();
try {
File dir = new File(location.toURI());
assertTrue(dir.getPath(), dir.exists());
File f = dir.getParentFile().getParentFile().getParentFile().getParentFile()
.getParentFile().getParentFile().getParentFile();
return new File(f, "out" + File.separator + "repo");
} catch (URISyntaxException e) {
fail(e.getLocalizedMessage());
}
return null;
}
/**
* Returns a string that contains the gradle buildscript content
*/
public String getGradleBuildscript() {
return "apply from: \"../commonHeader.gradle\"\n" +
"buildscript { apply from: \"../commonBuildScript" +
(experimentalMode ? "Experimental" : "") + ".gradle\", to: buildscript }\n" +
"\n" +
"apply from: \"../commonLocalRepo.gradle\"\n";
}
/**
* Return a list of all task names of the project.
*/
public List<String> getTaskList() {
ProjectConnection connection = getProjectConnection();
try {
GradleProject project = connection.getModel(GradleProject.class);
List<String> tasks = Lists.newArrayList();
for (GradleTask gradleTask : project.getTasks()) {
tasks.add(gradleTask.getName());
}
return tasks;
} finally {
connection.close();
}
}
/**
* Runs gradle on the project. Throws exception on failure.
*
* @param tasks Variadic list of tasks to execute.
*/
public void execute(String ... tasks) {
execute(Collections.<String>emptyList(), false, false, tasks);
}
public void execute(@NonNull List<String> arguments, String ... tasks) {
execute(arguments, false, false, tasks);
}
/**
* Runs gradle on the project, and returns the project model. Throws exception on failure.
*
* @param tasks Variadic list of tasks to execute.
*
* @return the AndroidProject model for the project.
*/
@NonNull
public AndroidProject executeAndReturnModel(String ... tasks) {
return executeAndReturnModel(false, tasks);
}
/**
* Runs gradle on the project, and returns the project model. Throws exception on failure.
*
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
* @param tasks Variadic list of tasks to execute.
*
* @return the AndroidProject model for the project.
*/
@NonNull
public AndroidProject executeAndReturnModel(boolean emulateStudio_1_0, String ... tasks) {
//noinspection ConstantConditions
return execute(Collections.<String>emptyList(), true, emulateStudio_1_0, tasks);
}
/**
* Runs gradle on the project, and returns a project model for each sub-project.
* Throws exception on failure.
*
* @param tasks Variadic list of tasks to execute.
*
* @return the AndroidProject model for the project.
*/
@NonNull
public Map<String, AndroidProject> executeAndReturnMultiModel(String ... tasks) {
return executeAndReturnMultiModel(false, tasks);
}
/**
* Runs gradle on the project, and returns a project model for each sub-project.
* Throws exception on failure.
*
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
* @param tasks Variadic list of tasks to execute.
*
* @return the AndroidProject model for the project.
*/
@NonNull
public Map<String, AndroidProject> executeAndReturnMultiModel(boolean emulateStudio_1_0, String ... tasks) {
ProjectConnection connection = getProjectConnection();
try {
executeBuild(Collections.<String>emptyList(), connection, tasks);
return buildModel(connection, new GetAndroidModelAction(), emulateStudio_1_0);
} finally {
connection.close();
}
}
/**
* Returns the project model without building.
*
* This will fail if the project is a multi-project setup or if there are any sync issues
* while loading the project.
*/
@NonNull
public AndroidProject getSingleModel() {
return getSingleModel(false /* emulateStudio_1_0 */, true /*assertNodSyncIssues */);
}
/**
* Returns the project model without building, querying it the way Studio 1.0 does.
*
* This will fail if the project is a multi-project setup or if there are any sync issues
* while loading the project.
*/
@NonNull
public AndroidProject getSingleModelAsStudio1() {
return getSingleModel(true /* emulateStudio_1_0 */, true /*assertNodSyncIssues */);
}
/**
* Returns the project model without building.
*
* This will fail if the project is a multi-project setup.
*/
@NonNull
public AndroidProject getSingleModelIgnoringSyncIssues() {
return getSingleModel(false /* emulateStudio_1_0 */, false /*assertNodSyncIssues */);
}
/**
* Returns the project model without building, querying it the way Studio 1.0 does.
*
* This will fail if the project is a multi-project setup.
*/
@NonNull
public AndroidProject getSingleModelIgnoringSyncIssuesAsStudio1() {
return getSingleModel(true /* emulateStudio_1_0 */, false /*assertNodSyncIssues */);
}
/**
* Returns the project model without building.
*
* This will fail if the project is a multi-project setup.
*
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
* @param assertNoSyncIssues true if the presence of sync issues during the model evaluation
* should raise a {@link AssertionError}s
*/
@NonNull
private AndroidProject getSingleModel(boolean emulateStudio_1_0, boolean assertNoSyncIssues) {
ProjectConnection connection = getProjectConnection();
try {
Map<String, AndroidProject> modelMap = buildModel(
connection,
new GetAndroidModelAction(),
emulateStudio_1_0);
// ensure there was only one project
assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
1, modelMap.size());
AndroidProject androidProject = modelMap.get(":");
if (assertNoSyncIssues) {
assertNoSyncIssues(androidProject);
}
return androidProject;
} finally {
connection.close();
}
}
/**
* Returns a project model for each sub-project without building generating a
* {@link AssertionError} if any sync issue is raised during the model loading.
*/
@NonNull
public Map<String, AndroidProject> getAllModels() {
Map<String, AndroidProject> allModels = getAllModels(new GetAndroidModelAction(), false);
for (AndroidProject project : allModels.values()) {
assertNoSyncIssues(project);
}
return allModels;
}
private static void assertNoSyncIssues(AndroidProject project) {
if (!project.getSyncIssues().isEmpty()) {
StringBuilder msg = new StringBuilder();
msg.append("Project ")
.append(project.getName())
.append(" had sync issues :\n");
for (SyncIssue syncIssue : project.getSyncIssues()) {
msg.append(syncIssue);
msg.append("\n");
}
fail(msg.toString());
}
}
/**
* Returns a project model for each sub-project without building and ignoring potential sync
* issues. Sync issues will still be returned for each {@link AndroidProject} that failed to
* sync properly.
*/
@NonNull
public Map<String, AndroidProject> getAllModelsIgnoringSyncIssues() {
return getAllModels(new GetAndroidModelAction(), false);
}
/**
* Returns a project model for each sub-project without building.
*
* @param action the build action to gather the model
*/
@NonNull
public <K, V> Map<K, V> getAllModels(@NonNull BuildAction<Map<K, V>> action) {
return getAllModels(action, false);
}
/**
* Returns a project model for each sub-project without building.
*
* @param action the build action to gather the model
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
*/
@NonNull
public <K, V> Map<K, V> getAllModels(
@NonNull BuildAction<Map<K, V>> action,
boolean emulateStudio_1_0) {
ProjectConnection connection = getProjectConnection();
try {
return buildModel(connection, action, emulateStudio_1_0);
} finally {
connection.close();
}
}
public void evaluate() {
getAllModels(new GetEmptyModelAction(), false);
}
/**
* Runs gradle on the project. Throws exception on failure.
*
* @param arguments List of arguments for the gradle command.
* @param returnModel whether the model should be queried and returned.
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
* @param tasks Variadic list of tasks to execute.
*
* @return the model, if <var>returnModel</var> was true, null otherwise
*/
@Nullable
private AndroidProject execute(
@NonNull List<String> arguments,
boolean returnModel,
boolean emulateStudio_1_0,
@NonNull String ... tasks) {
ProjectConnection connection = getProjectConnection();
try {
executeBuild(arguments, connection, tasks);
if (returnModel) {
Map<String, AndroidProject> modelMap = buildModel(
connection,
new GetAndroidModelAction(),
emulateStudio_1_0);
// ensure there was only one project
assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
1, modelMap.size());
return modelMap.get(":");
}
} finally {
connection.close();
}
return null;
}
private void executeBuild(List<String> arguments, ProjectConnection connection,
String[] tasks) {
List<String> args = Lists.newArrayListWithCapacity(3 + arguments.size());
args.add("-i");
args.add("-u");
args.addAll(arguments);
List<String> jvmArguments = getJvmArguments();
if (JacocoAgent.isJacocoEnabled()) {
jvmArguments.add(JacocoAgent.getJvmArg());
}
if (!jvmArguments.isEmpty()) {
args.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(jvmArguments));
}
BuildLauncher launcher = connection.newBuild().forTasks(tasks)
.withArguments(args.toArray(new String[args.size()]));
if (stdout != null) {
launcher.setStandardOutput(stdout);
} else {
launcher.setStandardOutput(System.out);
}
if (stderr != null) {
launcher.setStandardError(stderr);
} else {
launcher.setStandardError(System.err);
}
launcher.run();
}
private List<String> getJvmArguments() {
List<String> jvmArguments = new ArrayList<String>();
if (!Strings.isNullOrEmpty(heapSize)) {
jvmArguments.add("-Xmx" + heapSize);
}
jvmArguments.add("-XX:MaxPermSize=1024m");
String debugIntegrationTest = System.getenv("DEBUG_INNER_TEST");
if (!Strings.isNullOrEmpty(debugIntegrationTest)) {
jvmArguments.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005");
}
return jvmArguments;
}
/**
* Returns a project model for each sub-project without building.
*
* @param connection the opened ProjectConnection
* @param action the build action to gather the model
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
*/
@NonNull
private <K,V> Map<K, V> buildModel(
@NonNull ProjectConnection connection,
@NonNull BuildAction<Map<K, V>> action,
boolean emulateStudio_1_0) {
BuildActionExecuter<Map<K, V>> executer = connection.action(action);
List<String> arguments = Lists.newArrayListWithCapacity(emulateStudio_1_0 ? 2 : 3);
arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY + "=true");
arguments.add("-P" + AndroidProject.PROPERTY_INVOKED_FROM_IDE + "=true");
if (!emulateStudio_1_0) {
arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED + "=true");
}
List<String> debugJvmArguments = getJvmArguments();
if (!debugJvmArguments.isEmpty()) {
arguments.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(debugJvmArguments));
}
executer.withArguments(Iterables.toArray(arguments, String.class));
executer.setStandardOutput(System.out);
executer.setStandardError(System.err);
return executer.run();
}
/**
* Return the stdout from all execute command.
*/
public ByteArrayOutputStream getStdout() {
return stdout;
}
/**
* Return the stderr from all execute command.
*/
public ByteArrayOutputStream getStderr() {
return stderr;
}
/**
* Create a File object. getTestDir will be the base directory if a relative path is supplied.
*
* @param path Full path of the file. May be a relative path.
*/
public File file(String path) {
File result = new File(path);
if (result.isAbsolute()) {
return result;
} else {
return new File(testDir, path);
}
}
/**
* Returns the NDK folder as built from the Android source tree.
*/
private static File findNdkDir() {
String androidHome = System.getenv("ANDROID_NDK_HOME");
if (androidHome != null) {
File f = new File(androidHome);
if (f.isDirectory()) {
return f;
} else {
System.out.println("Failed to find NDK in ANDROID_NDK_HOME=" + androidHome);
}
}
return null;
}
/**
* Returns a Gradle project Connection
*/
@NonNull
private ProjectConnection getProjectConnection() {
GradleConnector connector = GradleConnector.newConnector();
// Limit daemon idle time for tests. 10 seconds is enough for another test
// to start and reuse the daemon.
((DefaultGradleConnector) connector).daemonMaxIdleTime(10, TimeUnit.SECONDS);
return connector
.useGradleVersion(targetGradleVersion)
.forProjectDirectory(testDir)
.connect();
}
private static File createLocalProp(
@NonNull File project,
@NonNull File sdkDir,
@Nullable File ndkDir) throws IOException, StreamException {
ProjectPropertiesWorkingCopy localProp = ProjectProperties.create(
project.getAbsolutePath(), ProjectProperties.PropertyType.LOCAL);
localProp.setProperty(ProjectProperties.PROPERTY_SDK, sdkDir.getAbsolutePath());
if (ndkDir != null) {
localProp.setProperty(ProjectProperties.PROPERTY_NDK, ndkDir.getAbsolutePath());
}
localProp.save();
return (File) localProp.getFile();
}
}