diff options
author | 2017-04-10 21:24:21 +0000 | |
---|---|---|
committer | 2017-04-11 10:50:24 +0200 | |
commit | 3334b253e8f0e59bbb3eb97a6de2a07dd4e64a25 (patch) | |
tree | a6b36951bc755a5c38f9701e620066d88d4b8795 /src/test/java/com/google | |
parent | 0e3381e92ec2060fb3d2fd16d113e065063ccc03 (diff) |
Open source AndroidBinaryMultidexTest.
This includes tests for the feature in https://github.com/bazelbuild/bazel/issues/1936.
RELNOTES: None
PiperOrigin-RevId: 152734391
Diffstat (limited to 'src/test/java/com/google')
3 files changed, 279 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMultidexTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMultidexTest.java new file mode 100644 index 0000000000..9153f0dca5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMultidexTest.java @@ -0,0 +1,110 @@ +// 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 com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests the multidex code of {@link com.google.devtools.build.lib.rules.android.AndroidBinary}. + */ +@RunWith(JUnit4.class) +public class AndroidBinaryMultidexTest extends AndroidMultidexBaseTest { + + /** + * Tests that when multidex = "off", a classes.dex file is generated directly + * from the input jar. + */ + @Test + public void testNonMultidexBuildStructure() throws Exception { + scratch.file("java/foo/BUILD", + "android_binary(", + " name = 'nomultidex',", + " srcs = ['a.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']),", + " multidex = 'off')"); + internalTestNonMultidexBuildStructure("//java/foo:nomultidex"); + } + + /** + * Tests that the default multidex setting is the same as when multidex = "off". + */ + @Test + public void testDefaultBuildStructure() throws Exception { + scratch.file("java/foo/BUILD", + "android_binary(", + " name = 'default',", + " srcs = ['a.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']))"); + internalTestNonMultidexBuildStructure("//java/foo:default"); + } + + @Test + public void testManualMainDexMode() throws Exception { + scratch.file("java/foo/main_dex_list.txt", + "android/A.class"); + scratch.file("java/foo/BUILD", + "android_binary(", + " name = 'manual_main_dex',", + " srcs = ['a.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']),", + " multidex = 'manual_main_dex',", + " main_dex_list = 'main_dex_list.txt')"); + internalTestMultidexBuildStructure( + "//java/foo:manual_main_dex", MultidexMode.MANUAL_MAIN_DEX); + } + + /** + * Tests that when multidex = "legacy", a classes.dex.zip file is generated from + * an intermediate file with multidex mode specified and a "--main-dex-list" dx flag + * filled out with appropriate input, This file is then filtered through a zip + * action to remove non-.dex files to produce the final output. + */ + @Test + public void testLegacyMultidexBuildStructure() throws Exception { + scratch.file("java/foo/BUILD", + "android_binary(", + " name = 'legacy',", + " srcs = ['a.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']),", + " multidex = 'legacy')"); + internalTestMultidexBuildStructure("//java/foo:legacy", MultidexMode.LEGACY); + } + + /** + * Tests that when multidex = "native", a classes.dex.zip file is generated from + * an intermediate file with multidex mode specified. Unlike in "legacy" mode, + * no actions are required to set and fill the "--main-dex-list" dx flag. The + * intermediate file is then filtered through a zip action to remove non-.dex files + * to produce the final output. + */ + @Test + public void testNativeMultidexBuildStructure() throws Exception { + scratch.file("java/foo/BUILD", + "android_binary(", + " name = 'native',", + " srcs = ['a.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']),", + " multidex = 'native')"); + internalTestMultidexBuildStructure("//java/foo:native", MultidexMode.NATIVE); + } +} + diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidMultidexBaseTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidMultidexBaseTest.java new file mode 100644 index 0000000000..272e4a90e1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidMultidexBaseTest.java @@ -0,0 +1,149 @@ +// 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.truth.Truth.assertThat; +import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; +import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode; +import java.util.Set; +import org.junit.Before; + +/** + * Base class for testing the multidex code of android_binary and android_test. + */ +public class AndroidMultidexBaseTest extends BuildViewTestCase { + + @Before + public final void createFiles() throws Exception { + scratch.file("java/android/BUILD", + "android_binary(name = 'app',", + " srcs = ['A.java'],", + " manifest = 'AndroidManifest.xml',", + " resource_files = glob(['res/**']),", + " )"); + scratch.file("java/android/res/values/strings.xml", + "<resources><string name = 'hello'>Hello Android!</string></resources>"); + scratch.file("java/android/A.java", + "package android; public class A {};"); + } + + /** + * Internal helper method: given an android_binary rule label, check that it builds in + * multidex mode. Three multidex variations are possible: "legacy", "manual_main_dex" and + * "native". + * + * @param ruleLabel the android_binary rule label to test against + * @param multidexMode the multidex mode used in the rule + */ + protected void internalTestMultidexBuildStructure( + String ruleLabel, MultidexMode multidexMode) throws Exception { + + ConfiguredTarget binary = getConfiguredTarget(ruleLabel); + Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)); + + // Always created: + Artifact intermediateDexOutput = + getFirstArtifactEndingWith(artifacts, "intermediate_classes.dex.zip"); + assertThat(intermediateDexOutput).isNotNull(); + Artifact finalDexOutput = getFirstArtifactEndingWith(artifacts, "/classes.dex.zip"); + assertThat(finalDexOutput).isNotNull(); + + // Only created in legacy mode: + Artifact strippedJar = getFirstArtifactEndingWith(artifacts, "main_dex_intermediate.jar"); + Artifact mainDexList = getFirstArtifactEndingWith(artifacts, "main_dex_list.txt"); + + if (multidexMode == MultidexMode.LEGACY) { + // First action: check that the stripped jar is generated through Proguard. + AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(getRuleContext(binary)); + assertThat(strippedJar).isNotNull(); + SpawnAction stripAction = (SpawnAction) getGeneratingAction(strippedJar); + assertThat(stripAction.getCommandFilename()) + .isEqualTo(sdk.getProguard().getExecutable().getExecPathString()); + + // Second action: The dexer consumes the stripped jar to create the main dex class list. + assertThat(mainDexList).isNotNull(); + SpawnAction mainDexAction = (SpawnAction) getGeneratingAction(mainDexList); + assertThat(mainDexAction.getArguments()).containsAllOf( + mainDexList.getExecPathString(), + strippedJar.getExecPathString()).inOrder(); + } else if (multidexMode == MultidexMode.MANUAL_MAIN_DEX) { + // Manual main dex mode: strippedJar shouldn't exist and mainDexList should be provided. + assertThat(strippedJar).isNull(); + assertThat(mainDexList).isNotNull(); + } else { + // Native mode: these shouldn't exist. + assertThat(strippedJar).isNull(); + assertThat(mainDexList).isNull(); + } + + // Third action: the dexer command consumes the main dex class list to generate the intermediate + // dex output. + SpawnAction dexAction = (SpawnAction) getGeneratingAction(intermediateDexOutput); + ImmutableList.Builder<String> argBuilder = ImmutableList.builder(); + argBuilder.add("--dex"); + if (multidexMode == MultidexMode.OFF) { + argBuilder.add("--num-threads=5"); + } else { + argBuilder.add("--multi-dex"); + + if (multidexMode == MultidexMode.LEGACY || multidexMode == MultidexMode.MANUAL_MAIN_DEX) { + argBuilder.add("--main-dex-list=" + mainDexList.getExecPathString()); + } + } + + argBuilder.add("--output=" + intermediateDexOutput.getExecPathString()); + + assertThat(dexAction.getArguments()).containsAllIn(argBuilder.build()).inOrder(); + + // Final action: the SingleJar command that consumes the intermediate dex out to produce the + // final dex out. + SpawnAction singleJarAction = getGeneratingSpawnAction(finalDexOutput); + assertThat(singleJarAction.getArguments()) + .containsAllOf( + "--exclude_build_data", + "--dont_change_compression", + "--sources", + intermediateDexOutput.getExecPathString(), + "--output", + finalDexOutput.getExecPathString(), + "--include_prefixes", + "classes") + .inOrder(); + } + + /** + * Internal helper method: given an android_binary rule label, check that it builds + * in non-multidex mode. + */ + protected void internalTestNonMultidexBuildStructure(String ruleLabel) throws Exception { + + ConfiguredTarget binary = getConfiguredTarget(ruleLabel); + Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)); + Artifact dexOutput = getFirstArtifactEndingWith(artifacts, "classes.dex"); + SpawnAction dexAction = (SpawnAction) getGeneratingAction(dexOutput); + + assertThat(dexAction.getArguments()).doesNotContain("--multi-dex"); + assertThat(dexAction.getArguments()).containsAllOf( + "--dex", + "--num-threads=5", + "--output=" + dexOutput.getExecPathString()).inOrder(); + } +} + diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/BUILD b/src/test/java/com/google/devtools/build/lib/rules/android/BUILD index b543ba853d..f7d7e1080e 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/android/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/android/BUILD @@ -129,3 +129,23 @@ java_test( "//third_party:truth", ], ) + +java_test( + name = "AndroidBinaryMultidexTest", + srcs = [ + "AndroidBinaryMultidexTest.java", + "AndroidMultidexBaseTest.java", + ], + deps = [ + "//src/main/java/com/google/devtools/build/lib:android-rules", + "//src/main/java/com/google/devtools/build/lib:build-base", + "//src/main/java/com/google/devtools/build/lib:syntax", + "//src/main/java/com/google/devtools/build/lib/actions", + "//src/test/java/com/google/devtools/build/lib:actions_testutil", + "//src/test/java/com/google/devtools/build/lib:analysis_testutil", + "//src/test/java/com/google/devtools/build/lib:packages_testutil", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) |