// 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.rules.android;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith;
import static org.junit.Assert.assertNotNull;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.truth.Truth;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.OutputFileConfiguredTarget;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.AndroidDeployInfo;
import com.google.devtools.build.lib.rules.java.JavaCompileAction;
import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider.OutputJar;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.Preconditions;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/** Common methods shared between Android related {@link BuildViewTestCase}s. */
public abstract class AndroidBuildViewTestCase extends BuildViewTestCase {
@Override
protected ConfiguredRuleClassProvider getRuleClassProvider() {
ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
TestRuleClassProvider.addStandardRules(builder);
return builder
// TODO(b/35097211): Remove this once the new testing rules are released.
.addRuleDefinition(new AndroidDeviceScriptFixtureRule())
.addRuleDefinition(new AndroidInstrumentationRule())
.build();
}
protected Iterable<Artifact> getNativeLibrariesInApk(ConfiguredTarget target) {
SpawnAction compressedUnsignedApkaction = getCompressedUnsignedApkAction(target);
ImmutableList.Builder<Artifact> result = ImmutableList.builder();
for (Artifact output : compressedUnsignedApkaction.getInputs()) {
if (!output.getExecPathString().endsWith(".so")) {
continue;
}
result.add(output);
}
return result.build();
}
protected Label getGeneratingLabelForArtifact(Artifact artifact) {
Action generatingAction = getGeneratingAction(artifact);
return generatingAction != null
? getGeneratingAction(artifact).getOwner().getLabel()
: null;
}
protected void assertNativeLibrariesCopiedNotLinked(
ConfiguredTarget target, String... expectedLibNames) {
Iterable<Artifact> copiedLibs = getNativeLibrariesInApk(target);
for (Artifact copiedLib : copiedLibs) {
assertWithMessage("Native libraries were linked to produce " + copiedLib)
.that(getGeneratingLabelForArtifact(copiedLib))
.isNotEqualTo(target.getLabel());
}
assertThat(artifactsToStrings(copiedLibs))
.containsAllIn(ImmutableSet.copyOf(Arrays.asList(expectedLibNames)));
}
protected String flagValue(String flag, List<String> args) {
assertThat(args).contains(flag);
return args.get(args.indexOf(flag) + 1);
}
/**
* The unsigned APK is created in two actions. The first action adds everything that needs to be
* unconditionally compressed in the APK. The second action adds everything else, preserving their
* compression.
*/
protected Artifact getCompressedUnsignedApk(ConfiguredTarget target) {
return artifactByPath(
actionsTestUtil().artifactClosureOf(getFinalUnsignedApk(target)),
"_unsigned.apk",
"_unsigned.apk");
}
protected SpawnAction getCompressedUnsignedApkAction(ConfiguredTarget target) {
return getGeneratingSpawnAction(getCompressedUnsignedApk(target));
}
protected Artifact getFinalUnsignedApk(ConfiguredTarget target) {
return getFirstArtifactEndingWith(
target.getProvider(FileProvider.class).getFilesToBuild(), "_unsigned.apk");
}
protected SpawnAction getFinalUnsignedApkAction(ConfiguredTarget target) {
return getGeneratingSpawnAction(getFinalUnsignedApk(target));
}
protected Artifact getResourceApk(ConfiguredTarget target) {
Artifact resourceApk =
getFirstArtifactEndingWith(
getGeneratingAction(getFinalUnsignedApk(target)).getInputs(), ".ap_");
assertThat(resourceApk).isNotNull();
return resourceApk;
}
protected void assertProguardUsed(ConfiguredTarget binary) {
assertNotNull("proguard.jar is not in the rule output",
actionsTestUtil().getActionForArtifactEndingWith(
getFilesToBuild(binary), "_proguard.jar"));
}
protected List<String> resourceArguments(ResourceContainer resource) {
return resourceGeneratingAction(resource).getArguments();
}
protected SpawnAction resourceGeneratingAction(ResourceContainer resource) {
return getGeneratingSpawnAction(resource.getApk());
}
protected static ResourceContainer getResourceContainer(ConfiguredTarget target) {
return getResourceContainer(target, /* transitive= */ false);
}
protected static ResourceContainer getResourceContainer(
ConfiguredTarget target, boolean transitive) {
Preconditions.checkNotNull(target);
final AndroidResourcesProvider provider = target.getProvider(AndroidResourcesProvider.class);
assertThat(provider).named("No android resources exported from the target.").isNotNull();
return getOnlyElement(
transitive
? provider.getTransitiveAndroidResources()
: provider.getDirectAndroidResources());
}
protected ActionAnalysisMetadata getResourceClassJarAction(final ConfiguredTarget target) {
JavaRuleOutputJarsProvider jarProvider = target.getProvider(JavaRuleOutputJarsProvider.class);
assertThat(jarProvider).isNotNull();
return getGeneratingAction(
Iterables.find(jarProvider.getOutputJars(), new Predicate<OutputJar>() {
@Override
public boolean apply(@Nullable OutputJar outputJar) {
assertThat(outputJar).isNotNull();
assertThat(outputJar.getClassJar()).isNotNull();
return outputJar.getClassJar().getFilename().equals(
target.getTarget().getName() + "_resources.jar");
}
}
).getClassJar()
);
}
// android resources related tests
protected void assertPrimaryResourceDirs(List<String> expectedPaths, List<String> actualArgs) {
assertThat(actualArgs).contains("--primaryData");
String actualFlagValue = actualArgs.get(actualArgs.indexOf("--primaryData") + 1);
assertThat(actualFlagValue).matches("[^:]*:[^:]*:[^:]*");
ImmutableList.Builder<String> actualPaths = ImmutableList.builder();
actualPaths.add(actualFlagValue.split(":")[0].split("#"));
assertThat(actualPaths.build()).containsAllIn(expectedPaths);
}
protected List<String> getDirectDependentResourceDirs(List<String> actualArgs) {
assertThat(actualArgs).contains("--directData");
String actualFlagValue = actualArgs.get(actualArgs.indexOf("--directData") + 1);
return getDependentResourceDirs(actualFlagValue);
}
protected List<String> getDependentResourceDirs(String actualFlagValue) {
ImmutableList.Builder<String> actualPaths = ImmutableList.builder();
for (String resourceDependency : actualFlagValue.split(",")) {
assertThat(actualFlagValue).matches("[^:]*:[^:]*:[^:]*:.*");
actualPaths.add(resourceDependency.split(":")[0].split("#"));
}
return actualPaths.build();
}
protected String execPathEndingWith(Iterable<Artifact> inputs, String suffix) {
return getFirstArtifactEndingWith(inputs, suffix).getExecPathString();
}
@Nullable
protected AndroidDeployInfo getAndroidDeployInfo(Artifact artifact) throws IOException {
Action generatingAction = getGeneratingAction(artifact);
if (generatingAction instanceof AndroidDeployInfoAction) {
AndroidDeployInfoAction writeAction = (AndroidDeployInfoAction) generatingAction;
return writeAction.getDeployInfo();
}
return null;
}
protected List<String> getProcessorNames(String outputTarget) throws Exception {
OutputFileConfiguredTarget out = (OutputFileConfiguredTarget)
getFileConfiguredTarget(outputTarget);
JavaCompileAction compileAction = (JavaCompileAction) getGeneratingAction(out.getArtifact());
return compileAction.getProcessorNames();
}
// Returns an artifact that will be generated when a rule has resources.
protected static Artifact getResourceArtifact(ConfiguredTarget target) {
// the last provider is the provider from the target.
if (target
.getConfiguration()
.getFragment(AndroidConfiguration.class)
.useParallelResourceProcessing()) {
return Iterables.getLast(target.getProvider(AndroidResourcesProvider.class)
.getDirectAndroidResources()).getJavaClassJar();
} else {
return Iterables.getLast(target.getProvider(AndroidResourcesProvider.class)
.getDirectAndroidResources()).getJavaSourceJar();
}
}
protected static void assertPrimaryResourceDirs(
ConfiguredTarget target, List<String> expectedPaths, List<String> actualArgs) {
assertThat(actualArgs).contains("--primaryData");
String actualFlagValue = actualArgs.get(actualArgs.indexOf("--primaryData") + 1);
if (target
.getConfiguration()
.getFragment(AndroidConfiguration.class)
.useParallelResourceProcessing()) {
assertThat(actualFlagValue).matches("[^;]*;[^;]*;.*");
ImmutableList.Builder<String> actualPaths = ImmutableList.builder();
actualPaths.add(actualFlagValue.split(";")[0].split("#"));
Truth.assertThat(actualPaths.build()).containsAllIn(expectedPaths);
} else {
assertThat(actualFlagValue).matches("[^:]*:[^:]*:.*");
ImmutableList.Builder<String> actualPaths = ImmutableList.builder();
actualPaths.add(actualFlagValue.split(":")[0].split("#"));
Truth.assertThat(actualPaths.build()).containsAllIn(expectedPaths);
}
}
}