|  | /* | 
|  | * Copyright 2012-present Facebook, Inc. | 
|  | * | 
|  | * 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.facebook.buck.android; | 
|  |  | 
|  | import static org.junit.Assert.assertEquals; | 
|  |  | 
|  | import com.facebook.buck.java.DefaultJavaLibraryRule; | 
|  | import com.facebook.buck.java.Keystore; | 
|  | import com.facebook.buck.java.PrebuiltJarRule; | 
|  | import com.facebook.buck.model.BuildTarget; | 
|  | import com.facebook.buck.model.BuildTargetFactory; | 
|  | import com.facebook.buck.model.BuildTargetPattern; | 
|  | import com.facebook.buck.rules.BuildRule; | 
|  | import com.facebook.buck.rules.BuildRuleResolver; | 
|  | import com.facebook.buck.rules.DependencyGraph; | 
|  | import com.facebook.buck.rules.FakeAbstractBuildRuleBuilderParams; | 
|  | import com.facebook.buck.testutil.RuleMap; | 
|  | import com.facebook.buck.util.BuckConstant; | 
|  | import com.google.common.collect.ImmutableSet; | 
|  |  | 
|  | import org.junit.Test; | 
|  |  | 
|  | public class AndroidTransitiveDependencyGraphTest { | 
|  |  | 
|  | /** | 
|  | * This is a regression test to ensure that an additional 1 second startup cost is not | 
|  | * re-introduced to fb4a. | 
|  | */ | 
|  | @Test | 
|  | public void testFindTransitiveDependencies() { | 
|  | BuildRuleResolver ruleResolver = new BuildRuleResolver(); | 
|  |  | 
|  | // Create an AndroidBinaryRule that transitively depends on two prebuilt JARs. One of the two | 
|  | // prebuilt JARs will be listed in the AndroidBinaryRule's no_dx list. | 
|  | PrebuiltJarRule guavaRule = ruleResolver.buildAndAddToIndex( | 
|  | PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//third_party/guava:guava")) | 
|  | .setBinaryJar("third_party/guava/guava-10.0.1.jar") | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | PrebuiltJarRule jsr305Rule = ruleResolver.buildAndAddToIndex( | 
|  | PrebuiltJarRule.newPrebuiltJarRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//third_party/jsr-305:jsr-305")) | 
|  | .setBinaryJar("third_party/jsr-305/jsr305.jar") | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  |  | 
|  | BuildRule ndkLibrary = ruleResolver.buildAndAddToIndex( | 
|  | NdkLibrary.newNdkLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//java/com/facebook/native_library:library")) | 
|  | .addSrc("Android.mk") | 
|  | .setIsAsset(false) | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | BuildRule prebuiltNativeLibraryBuild = ruleResolver.buildAndAddToIndex( | 
|  | PrebuiltNativeLibrary.newPrebuiltNativeLibrary(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//java/com/facebook/prebuilt_native_library:library")) | 
|  | .setNativeLibsDirectory("/java/com/facebook/prebuilt_native_library/libs") | 
|  | .setIsAsset(true) | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | DefaultJavaLibraryRule libraryRule = ruleResolver.buildAndAddToIndex( | 
|  | DefaultJavaLibraryRule.newJavaLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//java/src/com/facebook:example")) | 
|  | .addDep(guavaRule.getBuildTarget()) | 
|  | .addDep(jsr305Rule.getBuildTarget()) | 
|  | .addDep(prebuiltNativeLibraryBuild.getBuildTarget()) | 
|  | .addDep(ndkLibrary.getBuildTarget())); | 
|  |  | 
|  | AndroidResourceRule manifestRule = ruleResolver.buildAndAddToIndex( | 
|  | AndroidResourceRule.newAndroidResourceRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//java/src/com/facebook:res")) | 
|  | .setManifestFile("java/src/com/facebook/module/AndroidManifest.xml") | 
|  | .setAssetsDirectory("assets/")); | 
|  |  | 
|  | BuildTarget keystoreTarget = BuildTargetFactory.newInstance("//keystore:debug"); | 
|  | ruleResolver.buildAndAddToIndex( | 
|  | Keystore.newKeystoreBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(keystoreTarget) | 
|  | .setStore("keystore/debug.keystore") | 
|  | .setProperties("keystore/debug.keystore.properties") | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | AndroidBinaryRule binaryRule = ruleResolver.buildAndAddToIndex( | 
|  | AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(BuildTargetFactory.newInstance("//java/src/com/facebook:app")) | 
|  | .addClasspathDep(libraryRule.getBuildTarget()) | 
|  | .addClasspathDep(manifestRule.getBuildTarget()) | 
|  | .addBuildRuleToExcludeFromDex(BuildTargetFactory.newInstance("//third_party/guava:guava")) | 
|  | .setManifest("java/src/com/facebook/AndroidManifest.xml") | 
|  | .setTarget("Google Inc.:Google APIs:16") | 
|  | .setKeystore(keystoreTarget)); | 
|  |  | 
|  | // Verify that the correct transitive dependencies are found. | 
|  | DependencyGraph graph = RuleMap.createGraphFromBuildRules(ruleResolver); | 
|  | AndroidTransitiveDependencies transitiveDeps = binaryRule.findTransitiveDependencies(graph); | 
|  | AndroidDexTransitiveDependencies dexTransitiveDeps = | 
|  | binaryRule.findDexTransitiveDependencies(graph); | 
|  | assertEquals( | 
|  | "Because guava was passed to no_dx, it should not be in the classpathEntriesToDex list", | 
|  | ImmutableSet.of("third_party/jsr-305/jsr305.jar"), | 
|  | dexTransitiveDeps.classpathEntriesToDex); | 
|  | assertEquals( | 
|  | "Because guava was passed to no_dx, it should not be treated as a third-party JAR whose " + | 
|  | "resources need to be extracted and repacked in the APK. If this is done, then code in " + | 
|  | "the guava-10.0.1.dex.1.jar in the APK's assets/ tmp may try to load the resource " + | 
|  | "from the APK as a ZipFileEntry rather than as a resource within guava-10.0.1.dex.1.jar. " + | 
|  | "Loading a resource in this way could take substantially longer. Specifically, this was " + | 
|  | "observed to take over one second longer to load the resource in fb4a. Because the " + | 
|  | "resource was loaded on startup, this introduced a substantial regression in the startup " + | 
|  | "time for the fb4a app.", | 
|  | ImmutableSet.of("third_party/jsr-305/jsr305.jar"), | 
|  | dexTransitiveDeps.pathsToThirdPartyJars); | 
|  | assertEquals( | 
|  | "Because assets directory was passed an AndroidResourceRule it should be added to the " + | 
|  | "transitive dependencies", | 
|  | ImmutableSet.of("assets/"), | 
|  | transitiveDeps.assetsDirectories); | 
|  | assertEquals( | 
|  | "Because manifest file was passed an AndroidResourceRule it should be added to the " + | 
|  | "transitive dependencies", | 
|  | ImmutableSet.of("java/src/com/facebook/module/AndroidManifest.xml"), | 
|  | transitiveDeps.manifestFiles); | 
|  | assertEquals( | 
|  | "Because a native library was declared as a dependency, it should be added to the " + | 
|  | "transitive dependencies.", | 
|  | ImmutableSet.of(((NativeLibraryBuildable)ndkLibrary.getBuildable()).getLibraryPath()), | 
|  | transitiveDeps.nativeLibsDirectories); | 
|  | assertEquals( | 
|  | "Because a prebuilt native library  was declared as a dependency (and asset), it should " + | 
|  | "be added to the transitive dependecies.", | 
|  | ImmutableSet.of(((NativeLibraryBuildable)prebuiltNativeLibraryBuild.getBuildable()) | 
|  | .getLibraryPath()), | 
|  | transitiveDeps.nativeLibAssetsDirectories); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * If the keystore rule depends on an android_library, and an android_binary uses that keystore, | 
|  | * the keystore's android_library should not contribute to the classpath of the android_binary. | 
|  | */ | 
|  | @Test | 
|  | public void testGraphForAndroidBinaryExcludesKeystoreDeps() { | 
|  | BuildRuleResolver ruleResolver = new BuildRuleResolver(); | 
|  |  | 
|  | BuildTarget androidLibraryKeystoreTarget = new BuildTarget("//java/com/keystore/base", "base"); | 
|  | ruleResolver.buildAndAddToIndex( | 
|  | AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(androidLibraryKeystoreTarget) | 
|  | .addSrc("java/com/facebook/keystore/Base.java") | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | BuildTarget keystoreTarget = new BuildTarget("//keystore", "debug"); | 
|  | ruleResolver.buildAndAddToIndex( | 
|  | Keystore.newKeystoreBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(keystoreTarget) | 
|  | .setStore("keystore/debug.keystore") | 
|  | .setProperties("keystore/debug.keystore.properties") | 
|  | .addDep(androidLibraryKeystoreTarget) | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | BuildTarget androidLibraryTarget = new BuildTarget("//java/com/facebook/base", "base"); | 
|  | ruleResolver.buildAndAddToIndex( | 
|  | AndroidLibraryRule.newAndroidLibraryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(androidLibraryTarget) | 
|  | .addSrc("java/com/facebook/base/Base.java") | 
|  | .addVisibilityPattern(BuildTargetPattern.MATCH_ALL)); | 
|  |  | 
|  | AndroidBinaryRule androidBinaryRule = ruleResolver.buildAndAddToIndex( | 
|  | AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams()) | 
|  | .setBuildTarget(new BuildTarget("//apps/sample", "app")) | 
|  | .setManifest("apps/sample/AndroidManifest.xml") | 
|  | .setTarget("Google Inc.:Google APIs:16") | 
|  | .setKeystore(keystoreTarget) | 
|  | .addClasspathDep(androidLibraryTarget)); | 
|  |  | 
|  | DependencyGraph dependencyGraph = RuleMap.createGraphFromBuildRules(ruleResolver); | 
|  | AndroidDexTransitiveDependencies androidTransitiveDeps = androidBinaryRule | 
|  | .findDexTransitiveDependencies(dependencyGraph); | 
|  | assertEquals( | 
|  | "Classpath entries should include facebook/base but not keystore/base.", | 
|  | ImmutableSet.of(BuckConstant.GEN_DIR + "/java/com/facebook/base/lib__base__output/base.jar"), | 
|  | androidTransitiveDeps.classpathEntriesToDex); | 
|  | } | 
|  | } |