diff options
author | dannark <dannark@google.com> | 2018-12-31 08:17:04 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2017-12-31 08:19:06 -0800 |
commit | 2ef5d2170e0558468b0c0e989b910fbb52e95368 (patch) | |
tree | 1d72840b73d97f1a32e81bd29c961f44b04ea1cc /src/main | |
parent | 192583d5fbf49f43bc7646e5750647362dfaecfe (diff) |
Add android_local_test rule to Bazel.
This rule enables testing android_librarys locally in the jvm (as opposed to on a device). To use this rule with robolectric (robolectric.org), add the following to your WORKSPACE file:
http_archive(
name = "bazel_android",
url = "...",
)
load("@bazel_android//:setup_robolectric.bzl", "setup_robolectric")
setup_robolectric()
and then an android_local_test rule would need to add:
"@bazel_android//:robolectric",
to its dependencies.
android_local_test(
name = "MyTest",
srcs = ["MyTest.java"],
deps = [
"//java/app:lib",
"@bazel_android//:robolectric",
],
)
RELNOTES[NEW]: New android test rule, android_local_test.
PiperOrigin-RevId: 180438995
Diffstat (limited to 'src/main')
5 files changed, 261 insertions, 9 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 4ca45d08e8..07438ee69b 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -967,6 +967,7 @@ java_library( "rules/android/android_device_stub_template.txt", "rules/android/android_instrumentation_test_template.txt", "rules/android/databinding_annotation_template.txt", + "rules/android/robolectric_properties_template.txt", "rules/android/test_suite_property_name.txt", ], deps = [ diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 1acd502f82..5826e9987f 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -33,6 +33,7 @@ import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryRul import com.google.devtools.build.lib.bazel.rules.android.BazelAarImportRule; import com.google.devtools.build.lib.bazel.rules.android.BazelAndroidBinaryRule; import com.google.devtools.build.lib.bazel.rules.android.BazelAndroidLibraryRule; +import com.google.devtools.build.lib.bazel.rules.android.BazelAndroidLocalTestRule; import com.google.devtools.build.lib.bazel.rules.android.BazelAndroidSemantics; import com.google.devtools.build.lib.bazel.rules.common.BazelFilegroupRule; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcBinaryRule; @@ -485,6 +486,7 @@ public class BazelRuleClassProvider { builder.addRuleDefinition(new BazelAarImportRule()); builder.addRuleDefinition(new AndroidDeviceRule()); builder.addRuleDefinition(new AndroidLocalTestBaseRule()); + builder.addRuleDefinition(new BazelAndroidLocalTestRule()); builder.addSkylarkAccessibleTopLevels("android_common", new AndroidSkylarkCommon()); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTest.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTest.java new file mode 100644 index 0000000000..927467a6cd --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTest.java @@ -0,0 +1,141 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// 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.google.devtools.build.lib.bazel.rules.android; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.Runfiles; +import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.bazel.rules.java.BazelJavaSemantics; +import com.google.devtools.build.lib.rules.android.AndroidLocalTestBase; +import com.google.devtools.build.lib.rules.android.AndroidSdkProvider; +import com.google.devtools.build.lib.rules.android.AndroidSemantics; +import com.google.devtools.build.lib.rules.java.JavaCommon; +import com.google.devtools.build.lib.rules.java.JavaCompilationArgs.ClasspathType; +import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts.Builder; +import com.google.devtools.build.lib.rules.java.JavaCompilationHelper; +import com.google.devtools.build.lib.rules.java.JavaSemantics; +import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; +import com.google.devtools.build.lib.util.ShellEscaper; + +/** An implementation for the "android_local_test" rule. */ +public class BazelAndroidLocalTest extends AndroidLocalTestBase { + + Artifact androidAllJarsPropFile; + + @Override + protected AndroidSemantics createAndroidSemantics() { + return BazelAndroidSemantics.INSTANCE; + } + + @Override + protected JavaSemantics createJavaSemantics() { + return BazelJavaSemantics.INSTANCE; + } + + @Override + protected ImmutableList<String> getJvmFlags(RuleContext ruleContext, String testClass) + throws RuleErrorException { + Artifact androidAllJarsPropertiesFile = getAndroidAllJarsPropertiesFile(ruleContext); + + return ImmutableList.<String>builder() + .addAll(JavaCommon.getJvmFlags(ruleContext)) + .add("-ea") + .add("-Dbazel.test_suite=" + ShellEscaper.escapeString(testClass)) + .add("-Drobolectric.offline=true") + .add( + "-Drobolectric-deps.properties=" + androidAllJarsPropertiesFile.getRunfilesPathString()) + .build(); + } + + @Override + protected String getMainClass( + RuleContext ruleContext, + JavaSemantics javaSemantics, + JavaCompilationHelper helper, + Artifact executable, + Artifact instrumentationMetadata, + Builder javaArtifactsBuilder, + JavaTargetAttributes.Builder attributesBuilder) + throws InterruptedException, RuleErrorException { + // coverage does not yet work with android_local_test + if (ruleContext.getConfiguration().isCodeCoverageEnabled()) { + ruleContext.throwWithRuleError("android_local_test does not yet support coverage"); + } + return "com.google.testing.junit.runner.BazelTestRunner"; + } + + @Override + protected JavaCompilationHelper getJavaCompilationHelperWithDependencies( + RuleContext ruleContext, + JavaSemantics javaSemantics, + JavaCommon javaCommon, + JavaTargetAttributes.Builder javaTargetAttributesBuilder) { + + JavaCompilationHelper javaCompilationHelper = + new JavaCompilationHelper( + ruleContext, javaSemantics, javaCommon.getJavacOpts(), javaTargetAttributesBuilder); + javaCompilationHelper.addLibrariesToAttributes( + ImmutableList.copyOf(javaCommon.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY))); + + javaCompilationHelper.addLibrariesToAttributes( + ImmutableList.of(getAndCheckTestSupport(ruleContext))); + + javaTargetAttributesBuilder.setBootClassPath( + ImmutableList.of(AndroidSdkProvider.fromRuleContext(ruleContext).getAndroidJar())); + javaTargetAttributesBuilder.addRuntimeClassPathEntry( + AndroidSdkProvider.fromRuleContext(ruleContext).getAndroidJar()); + + return javaCompilationHelper; + } + + @Override + protected TransitiveInfoCollection getAndCheckTestSupport(RuleContext ruleContext) { + // Add the unit test support to the list of dependencies. + return Iterables.getOnlyElement(ruleContext.getPrerequisites("$testsupport", Mode.TARGET)); + } + + @Override + // Bazel needs the android-all jars properties file in order for robolectric to + // run. If it does not find it in the deps of the android_local_test rule, it will + // throw an error. + protected Artifact getAndroidAllJarsPropertiesFile(RuleContext ruleContext) + throws RuleErrorException { + if (androidAllJarsPropFile == null) { + androidAllJarsPropFile = getAndroidAllJarsPropertiesFileHelper(ruleContext); + } + return androidAllJarsPropFile; + } + + private Artifact getAndroidAllJarsPropertiesFileHelper(RuleContext ruleContext) + throws RuleErrorException { + Iterable<RunfilesProvider> runfilesProviders = + ruleContext.getPrerequisites("deps", Mode.TARGET, RunfilesProvider.class); + for (RunfilesProvider runfilesProvider : runfilesProviders) { + Runfiles dataRunfiles = runfilesProvider.getDataRunfiles(); + for (Artifact artifact : dataRunfiles.getAllArtifacts()) { + if (artifact.getFilename().equals("robolectric-deps.properties")) { + return artifact; + } + } + } + ruleContext.throwWithRuleError( + "'robolectric-deps.properties' not found in" + " the deps of the rule."); + return null; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTestRule.java new file mode 100644 index 0000000000..7f1bc69ff1 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTestRule.java @@ -0,0 +1,101 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// 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.google.devtools.build.lib.bazel.rules.android; + +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; +import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; +import static com.google.devtools.build.lib.packages.BuildType.TRISTATE; +import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromFunctions; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.bazel.rules.java.BazelJavaRuleClasses.BaseJavaBinaryRule; +import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; +import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; +import com.google.devtools.build.lib.packages.TriState; +import com.google.devtools.build.lib.rules.android.AndroidFeatureFlagSetProvider; +import com.google.devtools.build.lib.rules.android.AndroidLocalTestBaseRule; +import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagTransitionFactory; +import com.google.devtools.build.lib.rules.java.JavaConfiguration; +import com.google.devtools.build.lib.rules.java.JavaInfo; +import com.google.devtools.build.lib.rules.java.JavaSemantics; +import com.google.devtools.build.lib.rules.java.Jvm; + +/** Rule definition for Bazel android_local_test */ +public class BazelAndroidLocalTestRule implements RuleDefinition { + + protected static final String JUNIT_TESTRUNNER = "//tools/jdk:TestRunner_deploy.jar"; + + private static final ImmutableCollection<String> ALLOWED_RULES_IN_DEPS = + ImmutableSet.of( + "aar_import", + "android_library", + "java_import", + "java_library", + "java_lite_proto_library"); + + static final ImplicitOutputsFunction ANDROID_ROBOLECTRIC_IMPLICIT_OUTPUTS = + fromFunctions(JavaSemantics.JAVA_BINARY_CLASS_JAR, JavaSemantics.JAVA_BINARY_SOURCE_JAR); + + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + return builder + .requiresConfigurationFragments(JavaConfiguration.class, Jvm.class) + .setImplicitOutputsFunction(ANDROID_ROBOLECTRIC_IMPLICIT_OUTPUTS) + .override( + attr("deps", LABEL_LIST) + .allowedFileTypes() + .allowedRuleClasses(ALLOWED_RULES_IN_DEPS) + .mandatoryProvidersList( + ImmutableList.of( + ImmutableList.of( + SkylarkProviderIdentifier.forKey(JavaInfo.PROVIDER.getKey()))))) + .override(attr("$testsupport", LABEL).value(environment.getToolsLabel(JUNIT_TESTRUNNER))) + .override(attr("stamp", TRISTATE).value(TriState.NO)) + .removeAttribute("$experimental_testsupport") + .removeAttribute("classpath_resources") + .removeAttribute("create_executable") + .removeAttribute("deploy_manifest_lines") + .removeAttribute("distribs") + .removeAttribute("launcher") + .removeAttribute("main_class") + .removeAttribute("resources") + .removeAttribute("use_testrunner") + .removeAttribute(":java_launcher") + .cfg( + new ConfigFeatureFlagTransitionFactory(AndroidFeatureFlagSetProvider.FEATURE_FLAG_ATTR)) + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("android_local_test") + .type(RuleClassType.TEST) + .ancestors( + AndroidLocalTestBaseRule.class, + BaseJavaBinaryRule.class, + BaseRuleClasses.TestBaseRule.class) + .factoryClass(BazelAndroidLocalTest.class) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java index ad084013fc..338f878963 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java @@ -65,6 +65,7 @@ import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; /** A base implementation for the "android_local_test" rule. */ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactory { @@ -82,8 +83,7 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor // Use the regular Java javacopts. Enforcing android-compatible Java // (-source 7 -target 7 and no TWR) is unnecessary for robolectric tests // since they run on a JVM, not an android device. - JavaTargetAttributes.Builder attributesBuilder = - getJavaTargetAttributes(ruleContext, javaCommon); + JavaTargetAttributes.Builder attributesBuilder = javaCommon.initCommon(); // Create the final merged manifest ResourceDependencies resourceDependencies = @@ -351,7 +351,6 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor private void compileResourceJar( RuleContext ruleContext, ResourceApk resourceApk, Artifact resourceClassJar) throws InterruptedException, RuleErrorException { - if (resourceApk.getResourceJavaClassJar() == null) { new RClassGeneratorActionBuilder(ruleContext) .targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)) @@ -435,6 +434,12 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor if (ruleContext.isAttrDefined("$robolectric", LABEL_LIST)) { depsForRunfiles.addAll(ruleContext.getPrerequisites("$robolectric", Mode.TARGET)); } + + Artifact androidAllJarsPropertiesFile = getAndroidAllJarsPropertiesFile(ruleContext); + if (androidAllJarsPropertiesFile != null) { + builder.addArtifact(androidAllJarsPropertiesFile); + } + depsForRunfiles.addAll(ruleContext.getPrerequisites("runtime_deps", Mode.TARGET)); builder.addArtifacts(getRuntimeJarsForTargets(getAndCheckTestSupport(ruleContext))); @@ -520,12 +525,9 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor /** Get AndroidSemantics */ protected abstract AndroidSemantics createAndroidSemantics(); - /** Get JavaTargetAttributes Builder */ - protected abstract JavaTargetAttributes.Builder getJavaTargetAttributes( - RuleContext ruleContext, JavaCommon javaCommon); - /** Set test and robolectric specific jvm flags */ - protected abstract ImmutableList<String> getJvmFlags(RuleContext ruleContext, String testClass); + protected abstract ImmutableList<String> getJvmFlags(RuleContext ruleContext, String testClass) + throws RuleErrorException; /** Return the testrunner main class */ protected abstract String getMainClass( @@ -536,7 +538,7 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor Artifact instrumentationMetadata, JavaCompilationArtifacts.Builder javaArtifactsBuilder, JavaTargetAttributes.Builder attributesBuilder) - throws InterruptedException; + throws InterruptedException, RuleErrorException; /** * Add compilation dependencies to the java compilation helper. @@ -553,4 +555,9 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor /** Get the testrunner from the rule */ protected abstract TransitiveInfoCollection getAndCheckTestSupport(RuleContext ruleContext) throws RuleErrorException; + + /** Get the android-all jars properties file from the deps */ + @Nullable + protected abstract Artifact getAndroidAllJarsPropertiesFile(RuleContext ruleContext) + throws RuleErrorException; } |