Created
December 22, 2020 19:23
-
-
Save autonomousapps/e9821b0e187425df151dba550f318ddf to your computer and use it in GitHub Desktop.
buildSrc/src/main/java/com/android/build/gradle/tasks/factory/AndroidUnitTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//buildSrc/src/main/java/com/android/build/gradle/tasks/factory/AndroidUnitTest.java | |
package com.android.build.gradle.tasks.factory; | |
import com.android.annotations.NonNull; | |
import com.android.annotations.Nullable; | |
import com.android.build.api.artifact.impl.ArtifactsImpl; | |
import com.android.build.api.component.TestComponentProperties; | |
import com.android.build.api.component.impl.ComponentPropertiesImpl; | |
import com.android.build.api.component.impl.UnitTestPropertiesImpl; | |
import com.android.build.api.variant.impl.VariantPropertiesImpl; | |
import com.android.build.gradle.BaseExtension; | |
import com.android.build.gradle.internal.SdkComponentsBuildService; | |
import com.android.build.gradle.internal.publishing.AndroidArtifacts; | |
import com.android.build.gradle.internal.scope.BootClasspathBuilder; | |
import com.android.build.gradle.internal.scope.GlobalScope; | |
import com.android.build.gradle.internal.scope.InternalArtifactType; | |
import com.android.build.gradle.internal.tasks.VariantAwareTask; | |
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction; | |
import com.android.build.gradle.options.BooleanOption; | |
import com.android.build.gradle.tasks.GenerateTestConfig; | |
import com.android.builder.core.VariantType; | |
import com.google.common.collect.ImmutableList; | |
import org.gradle.api.file.ConfigurableFileCollection; | |
import org.gradle.api.plugins.JavaBasePlugin; | |
import org.gradle.api.reporting.ConfigurableReport; | |
import org.gradle.api.tasks.Internal; | |
import org.gradle.api.tasks.Nested; | |
import org.gradle.api.tasks.Optional; | |
import org.gradle.api.tasks.TaskAction; | |
import org.gradle.api.tasks.testing.Test; | |
import org.gradle.api.tasks.testing.TestTaskReports; | |
import org.jetbrains.annotations.NotNull; | |
import java.io.File; | |
import java.util.concurrent.Callable; | |
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL; | |
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.CLASSES_JAR; | |
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH; | |
public abstract class AndroidUnitTest extends Test implements VariantAwareTask { | |
private String variantName; | |
@Nullable private GenerateTestConfig.TestConfigInputs testConfigInputs; | |
@Internal | |
@NotNull @Override public String getVariantName() { | |
return variantName; | |
} | |
@Override public void setVariantName(@NotNull String s) { | |
variantName = s; | |
} | |
@Nested | |
@Optional | |
public GenerateTestConfig.TestConfigInputs getTestConfigInputs() { | |
return testConfigInputs; | |
} | |
@Override | |
@TaskAction | |
public void executeTests() { | |
getLogger().quiet("Hello world!"); | |
} | |
@SuppressWarnings("UnstableApiUsage") public static class CreationAction extends VariantTaskCreationAction<AndroidUnitTest, ComponentPropertiesImpl> { | |
@NonNull private final UnitTestPropertiesImpl unitTestProperties; | |
public CreationAction(@NonNull UnitTestPropertiesImpl unitTestProperties) { | |
super(unitTestProperties); | |
this.unitTestProperties = unitTestProperties; | |
} | |
@NotNull @Override public String getName() { | |
return computeTaskName(VariantType.UNIT_TEST_PREFIX); | |
} | |
@NotNull @Override public Class<AndroidUnitTest> getType() { | |
return AndroidUnitTest.class; | |
} | |
@Override | |
public void configure(@NonNull AndroidUnitTest task) { | |
super.configure(task); | |
GlobalScope globalScope = creationConfig.getGlobalScope(); | |
BaseExtension extension = globalScope.getExtension(); | |
VariantPropertiesImpl testedVariant = | |
(VariantPropertiesImpl) | |
((TestComponentProperties) creationConfig).getTestedVariant(); | |
boolean includeAndroidResources = | |
extension.getTestOptions().getUnitTests().isIncludeAndroidResources(); | |
boolean useRelativePathInTestConfig = | |
creationConfig | |
.getServices() | |
.getProjectOptions() | |
.get(BooleanOption.USE_RELATIVE_PATH_IN_TEST_CONFIG); | |
// we run by default in headless mode, so the forked JVM doesn't steal focus. | |
task.systemProperty("java.awt.headless", "true"); | |
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP); | |
task.setDescription("Run unit tests for the " + testedVariant.getName() + " build."); | |
task.setTestClassesDirs(creationConfig.getArtifacts().getAllClasses()); | |
task.setClasspath(computeClasspath(creationConfig, includeAndroidResources)); | |
if (includeAndroidResources) { | |
// When computing the classpath above, we made sure this task depends on the output | |
// of the GenerateTestConfig task. However, it is not enough. The GenerateTestConfig | |
// task has 2 types of inputs: direct inputs and indirect inputs. Only the direct | |
// inputs are registered with Gradle, whereas the indirect inputs are not (see that | |
// class for details). | |
// Since this task also depends on the indirect inputs to the GenerateTestConfig | |
// task, we also need to register those inputs with Gradle. | |
task.testConfigInputs = new GenerateTestConfig.TestConfigInputs(unitTestProperties); | |
} | |
// Put the variant name in the report path, so that different testing tasks don't | |
// overwrite each other's reports. For component model plugin, the report tasks are not | |
// yet configured. We get a hardcoded value matching Gradle's default. This will | |
// eventually be replaced with the new Java plugin. | |
TestTaskReports testTaskReports = task.getReports(); | |
ConfigurableReport xmlReport = testTaskReports.getJunitXml(); | |
xmlReport.setDestination(new File(globalScope.getTestResultsFolder(), task.getName())); | |
ConfigurableReport htmlReport = testTaskReports.getHtml(); | |
htmlReport.setDestination(new File(globalScope.getTestReportFolder(), task.getName())); | |
extension.getTestOptions().getUnitTests().applyConfiguration(task); | |
// The task is not yet cacheable when includeAndroidResources=true and | |
// android.testConfig.useRelativePath=false (bug 115873047). We set it explicitly here | |
// so Gradle doesn't have to store cache entries that won't be reused. | |
task.getOutputs() | |
.doNotCacheIf( | |
"AndroidUnitTest task is not yet cacheable" | |
+ " when includeAndroidResources=true" | |
+ " and android.testConfig.useRelativePath=false", | |
(thisTask) -> includeAndroidResources && !useRelativePathInTestConfig); | |
} | |
@NonNull | |
private ConfigurableFileCollection computeClasspath( | |
ComponentPropertiesImpl component, boolean includeAndroidResources) { | |
GlobalScope globalScope = component.getGlobalScope(); | |
ArtifactsImpl artifacts = component.getArtifacts(); | |
ConfigurableFileCollection collection = component.getServices().fileCollection(); | |
// the test classpath is made up of: | |
// 1. the config file | |
if (includeAndroidResources) { | |
collection.from( | |
artifacts.get(InternalArtifactType.UNIT_TEST_CONFIG_DIRECTORY.INSTANCE)); | |
} | |
// 2. the test component classes and java_res | |
collection.from(component.getArtifacts().getAllClasses()); | |
// TODO is this the right thing? this doesn't include the res merging via transform | |
// AFAIK | |
collection.from(artifacts.get(InternalArtifactType.JAVA_RES.INSTANCE)); | |
// 3. the runtime dependencies for both CLASSES and JAVA_RES type | |
collection.from( | |
component | |
.getVariantDependencies() | |
.getArtifactFileCollection(RUNTIME_CLASSPATH, ALL, CLASSES_JAR)); | |
collection.from( | |
component | |
.getVariantDependencies() | |
.getArtifactFileCollection( | |
RUNTIME_CLASSPATH, ALL, AndroidArtifacts.ArtifactType.JAVA_RES)); | |
// 4. The separately compile R class, if applicable. | |
if (!globalScope.getExtension().getAaptOptions().getNamespaced()) { | |
collection.from(component.getVariantScope().getRJarForUnitTests()); | |
} | |
// 5. Any additional or requested optional libraries | |
collection.from(getAdditionalAndRequestedOptionalLibraries(component.getGlobalScope())); | |
// 6. Mockable JAR is last, to make sure you can shadow the classes with | |
// dependencies. | |
collection.from(component.getGlobalScope().getMockableJarArtifact()); | |
return collection; | |
} | |
@NonNull | |
private ConfigurableFileCollection getAdditionalAndRequestedOptionalLibraries( | |
GlobalScope globalScope) { | |
return creationConfig | |
.getServices() | |
.fileCollection( | |
(Callable) | |
() -> | |
BootClasspathBuilder.INSTANCE | |
.computeAdditionalAndRequestedOptionalLibraries( | |
globalScope.getProject(), | |
globalScope | |
.getSdkComponents() | |
.flatMap( | |
SdkComponentsBuildService | |
::getAdditionalLibrariesProvider) | |
.get(), | |
globalScope | |
.getSdkComponents() | |
.flatMap( | |
SdkComponentsBuildService | |
::getOptionalLibrariesProvider) | |
.get(), | |
false, | |
ImmutableList.copyOf( | |
globalScope | |
.getExtension() | |
.getLibraryRequests()), | |
creationConfig | |
.getServices() | |
.getIssueReporter())); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment