blob: d082805a4b5c19d7b924c0c0bec9745a3a1a0647 [file] [log] [blame]
/*
* Copyright (C) 2013 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.internal.tasks;
import static com.android.builder.core.BuilderConstants.CONNECTED;
import static com.android.builder.core.BuilderConstants.DEVICE;
import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS;
import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS;
import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.tasks.factory.TaskCreationAction;
import com.android.build.gradle.internal.test.report.ReportType;
import com.android.build.gradle.internal.test.report.TestReport;
import com.android.build.gradle.internal.utils.HasConfigurableValuesKt;
import com.android.builder.core.VariantType;
import com.android.utils.FileUtils;
import com.google.common.collect.Lists;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import org.gradle.internal.logging.ConsoleRenderer;
/** Task doing test report aggregation. */
public abstract class AndroidReportTask extends DefaultTask implements AndroidTestTask {
private final List<AndroidTestTask> subTasks = Lists.newArrayList();
private ReportType reportType;
private boolean ignoreFailures;
private boolean testFailed;
@OutputDirectory
public abstract DirectoryProperty getReportsDir();
@Override
@OutputDirectory
public abstract DirectoryProperty getResultsDir();
@Override
@Internal // This is an output, not an input of this task
public boolean getTestFailed() {
return testFailed;
}
@Override
public boolean getIgnoreFailures() {
return ignoreFailures;
}
@Override
public void setIgnoreFailures(boolean ignoreFailures) {
this.ignoreFailures = ignoreFailures;
}
@Input
public ReportType getReportType() {
return reportType;
}
public void setReportType(ReportType reportType) {
this.reportType = reportType;
}
public void addTask(AndroidTestTask task) {
subTasks.add(task);
this.dependsOn(task);
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public List<File> getResultsDirectories() {
return subTasks.stream()
.map(t -> t.getResultsDir().get().getAsFile())
.collect(Collectors.toList());
}
/**
* Sets that this current task will run and therefore needs to tell its children
* class to not stop on failures.
*/
public void setWillRun() {
for (AndroidTestTask task : subTasks) {
task.setIgnoreFailures(true);
}
}
@TaskAction
public void createReport() throws IOException {
File resultsOutDir = getResultsDir().get().getAsFile();
File reportOutDir = getResultsDir().get().getAsFile();
// empty the folders
FileUtils.cleanOutputDir(resultsOutDir);
FileUtils.cleanOutputDir(reportOutDir);
// do the copy.
copyResults(resultsOutDir);
// create the report.
TestReport report = new TestReport(reportType, resultsOutDir, reportOutDir);
report.generateReport();
// fail if any of the tasks failed.
for (AndroidTestTask task : subTasks) {
if (task.getTestFailed()) {
testFailed = true;
String reportUrl = new ConsoleRenderer().asClickableFileUrl(
new File(reportOutDir, "index.html"));
String message = "There were failing tests. See the report at: " + reportUrl;
if (getIgnoreFailures()) {
getLogger().warn(message);
} else {
throw new GradleException(message);
}
break;
}
}
}
private void copyResults(File reportOutDir) throws IOException {
List<File> resultDirectories = getResultsDirectories();
for (File directory : resultDirectories) {
FileUtils.copyDirectory(directory, reportOutDir);
}
}
public static class CreationAction extends TaskCreationAction<AndroidReportTask> {
public enum TaskKind { CONNECTED, DEVICE_PROVIDER }
private final GlobalScope scope;
private final TaskKind taskKind;
public CreationAction(@NonNull GlobalScope scope, @NonNull TaskKind taskKind) {
this.scope = scope;
this.taskKind = taskKind;
}
@NonNull
@Override
public String getName() {
return (taskKind == TaskKind.CONNECTED ? CONNECTED : DEVICE)
+ VariantType.ANDROID_TEST_SUFFIX;
}
@NonNull
@Override
public Class<AndroidReportTask> getType() {
return AndroidReportTask.class;
}
@Override
public void configure(@NonNull AndroidReportTask task) {
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
task.setDescription((taskKind == TaskKind.CONNECTED) ?
"Installs and runs instrumentation tests for all flavors on connected devices.":
"Installs and runs instrumentation tests using all Device Providers.");
task.setReportType(ReportType.MULTI_FLAVOR);
final String defaultReportsDir = scope.getReportsDir().getAbsolutePath()
+ "/" + FD_ANDROID_TESTS;
final String defaultResultsDir = scope.getOutputsDir().getAbsolutePath()
+ "/" + FD_ANDROID_RESULTS;
final String subfolderName =
taskKind == TaskKind.CONNECTED ? "/connected/" : "/devices/";
HasConfigurableValuesKt.setDisallowChanges(
task.getResultsDir(),
scope.getProject()
.provider(
() -> {
String dir =
scope.getExtension()
.getTestOptions()
.getResultsDir();
String rootLocation =
dir != null && !dir.isEmpty()
? dir
: defaultResultsDir;
return scope.getProject()
.getLayout()
.getProjectDirectory()
.dir(rootLocation + subfolderName + FD_FLAVORS_ALL);
}));
HasConfigurableValuesKt.setDisallowChanges(
task.getReportsDir(),
scope.getProject()
.provider(
() -> {
String dir =
scope.getExtension()
.getTestOptions()
.getReportDir();
String rootLocation =
dir != null && !dir.isEmpty()
? dir
: defaultReportsDir;
return scope.getProject()
.getLayout()
.getProjectDirectory()
.dir(rootLocation + subfolderName + FD_FLAVORS_ALL);
}));
}
}
}