private void createAndroidBinaryRuleAndTestCopyNativeLibraryCommand(
      ImmutableSet<String> cpuFilters,
      String sourceDir,
      String destinationDir,
      ImmutableList<String> expectedShellCommands) {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();
    AndroidBinaryRule.Builder builder =
        AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
            .setBuildTarget(BuildTargetFactory.newInstance("//:fbandroid_with_dash_debug_fbsign"))
            .setManifest("AndroidManifest.xml")
            .setKeystore(addKeystoreRule(ruleResolver))
            .setTarget("Google Inc:Google APIs:16");

    for (String filter : cpuFilters) {
      builder.addCpuFilter(filter);
    }

    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    AndroidBinaryRule buildRule = ruleResolver.buildAndAddToIndex(builder);
    buildRule.copyNativeLibrary(sourceDir, destinationDir, commands);

    ImmutableList<Step> steps = commands.build();

    assertEquals(steps.size(), expectedShellCommands.size());
    ExecutionContext context = createMock(ExecutionContext.class);
    replay(context);

    for (int i = 0; i < steps.size(); ++i) {
      Iterable<String> observedArgs = ((BashStep) steps.get(i)).getShellCommand(context);
      String observedCommand = Joiner.on(' ').join(observedArgs);
      assertEquals(expectedShellCommands.get(i), observedCommand);
    }

    verify(context);
  }
  @Test
  public void testGetInputsToCompareToOutput() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();
    AndroidBinaryRule.Builder androidBinaryRuleBuilder =
        AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
            .setBuildTarget(BuildTargetFactory.newInstance("//java/src/com/facebook:app"))
            .setManifest("java/src/com/facebook/AndroidManifest.xml")
            .setTarget("Google Inc.:Google APIs:16")
            .setKeystore(addKeystoreRule(ruleResolver));

    BuildContext context = createMock(BuildContext.class);
    replay(context);

    MoreAsserts.assertListEquals(
        "getInputsToCompareToOutput() should include manifest.",
        ImmutableList.of("java/src/com/facebook/AndroidManifest.xml"),
        ruleResolver.buildAndAddToIndex(androidBinaryRuleBuilder).getInputsToCompareToOutput());

    SourcePath proguardConfig = new FileSourcePath("java/src/com/facebook/proguard.cfg");
    androidBinaryRuleBuilder.setProguardConfig(Optional.of(proguardConfig));
    MoreAsserts.assertListEquals(
        "getInputsToCompareToOutput() should include Proguard config, if present.",
        ImmutableList.of(
            "java/src/com/facebook/AndroidManifest.xml", "java/src/com/facebook/proguard.cfg"),
        ruleResolver.buildAndAddToIndex(androidBinaryRuleBuilder).getInputsToCompareToOutput());

    verify(context);
  }
  /**
   * Tests an android_binary with zero dependent android_library rules that contains an assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithZeroAssetsDirectories() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, neither with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories = ImmutableSet.of();
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) {
            throw new RuntimeException("Unexpected: no assets directories to traverse!");
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that no assets/ directory is used.
    assertFalse(
        "There should not be an assets/ directory to pass to aapt.",
        allAssetsDirectory.isPresent());
    assertTrue(
        "There should not be any commands to build up an assets/ directory.",
        commands.build().isEmpty());
  }
  @Test
  public void testGetUnsignedApkPath() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    AndroidBinaryRule ruleInRootDirectory =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(BuildTargetFactory.newInstance("//:fb4a"))
                .setManifest("AndroidManifest.xml")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setTarget("Google Inc.:Google APIs:16"));
    assertEquals(GEN_DIR + "/fb4a.apk", ruleInRootDirectory.getApkPath());

    AndroidBinaryRule ruleInNonRootDirectory =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(BuildTargetFactory.newInstance("//java/com/example:fb4a"))
                .setManifest("AndroidManifest.xml")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setTarget("Google Inc.:Google APIs:16"));
    assertEquals(GEN_DIR + "/java/com/example/fb4a.apk", ruleInNonRootDirectory.getApkPath());
  }
  @Test
  public void testDexingCommand() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();
    AndroidBinaryRule splitDexRule =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(
                    BuildTargetFactory.newInstance("//:fbandroid_with_dash_debug_fbsign"))
                .setManifest("AndroidManifest.xml")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setTarget("Google Inc.:Google APIs:16")
                .setDexSplitMode(
                    new DexSplitMode(
                        /* shouldSplitDex */ true,
                        ZipSplitter.DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE,
                        DexStore.JAR,
                        /* useLinearAllocSplitDex */ false)));

    Set<String> classpath = Sets.newHashSet();
    ImmutableSet.Builder<String> secondaryDexDirectories = ImmutableSet.builder();
    ImmutableList.Builder<Step> commandsBuilder = ImmutableList.builder();
    String primaryDexPath = BIN_DIR + "/.dex/classes.dex";
    splitDexRule.addDexingCommands(
        classpath,
        secondaryDexDirectories,
        commandsBuilder,
        primaryDexPath,
        /* sourcePathResolver */ new Function<SourcePath, Path>() {
          @Override
          public Path apply(SourcePath input) {
            throw new UnsupportedOperationException("This resolver should not be used.");
          }
        });

    assertEquals(
        "Expected 2 new assets paths (one for metadata.txt and the other for the "
            + "secondary zips)",
        2,
        secondaryDexDirectories.build().size());

    List<Step> steps = commandsBuilder.build();
    assertCommandsInOrder(
        steps, ImmutableList.<Class<?>>of(SplitZipStep.class, SmartDexingStep.class));
  }
  @Test
  public void testGetProguardOutputFromInputClasspath() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    AndroidBinaryRule rule =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(
                    BuildTargetFactory.newInstance("//:fbandroid_with_dash_debug_fbsign"))
                .setManifest("AndroidManifest.xml")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setTarget("Google Inc.:Google APIs:16"));

    String proguardDir =
        rule.getProguardOutputFromInputClasspath(
            BIN_DIR + "/first-party/orca/lib-base/lib__lib-base__classes");
    assertEquals(
        GEN_DIR
            + "/.proguard/fbandroid_with_dash_debug_fbsign/"
            + BIN_DIR
            + "/first-party/orca/lib-base/lib__lib-base__classes-obfuscated.jar",
        proguardDir);
  }
  @Test
  public void testAndroidBinaryNoDx() {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, neither with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .addBuildRuleToExcludeFromDex(
                    BuildTargetFactory.newInstance("//java/src/com/facebook/base:libraryTwo"))
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    DependencyGraph graph = RuleMap.createGraphFromBuildRules(ruleResolver);
    AndroidTransitiveDependencies transitiveDependencies =
        androidBinary.findTransitiveDependencies(graph);
    AndroidDexTransitiveDependencies dexTransitiveDependencies =
        androidBinary.findDexTransitiveDependencies(graph);
    ImmutableList.Builder<Step> commands = ImmutableList.builder();

    BuildContext context = createMock(BuildContext.class);
    replay(context);
    androidBinary.addProguardCommands(
        context,
        dexTransitiveDependencies.classpathEntriesToDex,
        transitiveDependencies.proguardConfigs,
        commands,
        ImmutableSet.<String>of());
    verify(context);

    MakeCleanDirectoryStep expectedClean =
        new MakeCleanDirectoryStep("buck-out/gen/java/src/com/facebook/base/.proguard/apk");

    GenProGuardConfigStep expectedGenProguard =
        new GenProGuardConfigStep(
            "buck-out/bin/java/src/com/facebook/base/__manifest_apk__/AndroidManifest.xml",
            ImmutableSet.<String>of(),
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt");

    ProGuardObfuscateStep expectedObfuscation =
        new ProGuardObfuscateStep(
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt",
            ImmutableSet.<String>of(),
            false,
            ImmutableMap.of(
                "buck-out/gen/java/src/com/facebook/base/lib__libraryOne__output/libraryOne.jar",
                "buck-out/gen/java/src/com/facebook/base/.proguard/apk/buck-out/gen/java/src/com/"
                    + "facebook/base/lib__libraryOne__output/libraryOne-obfuscated.jar"),
            ImmutableSet.of(
                "buck-out/gen/java/src/com/facebook/base/lib__libraryTwo__output/libraryTwo.jar"),
            "buck-out/gen/java/src/com/facebook/base/.proguard/apk");

    assertEquals(
        ImmutableList.of(expectedClean, expectedGenProguard, expectedObfuscation),
        commands.build());
  }
  /**
   * Tests an android_binary with multiple dependent android_library rules, each with its own assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithMultipleAssetsDirectories() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, each with an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets1",
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets2",
            null /* nativeLibsDirectory */);

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    AndroidResourceRule resourceOne =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryOne_resources"));
    AndroidResourceRule resourceTwo =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryTwo_resources"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories =
        ImmutableSet.of(resourceOne.getAssets(), resourceTwo.getAssets());
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) throws IOException {
            String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath());
            if ("java/src/com/facebook/base/assets1".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets1", "guava-10.0.1-fork.dex.1.jar"),
                  "guava-10.0.1-fork.dex.1.jar");
            } else if ("java/src/com/facebook/base/assets2".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"),
                  "fonts/Theinhardt-Medium.otf");
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"),
                  "fonts/Theinhardt-Regular.otf");
            } else {
              throw new RuntimeException("Unexpected path: " + rootPath);
            }
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that an assets/ directory will be created and passed to aapt.
    assertTrue(allAssetsDirectory.isPresent());
    assertEquals(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__", allAssetsDirectory.get());
    List<? extends Step> expectedCommands =
        ImmutableList.of(
            new MakeCleanDirectoryStep(BIN_DIR + "/java/src/com/facebook/base/__assets_apk__"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets1/guava-10.0.1-fork.dex.1.jar",
                BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/guava-10.0.1-fork.dex.1.jar"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets2/fonts/Theinhardt-Medium.otf",
                BIN_DIR + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Medium.otf"),
            new MkdirAndSymlinkFileStep(
                "java/src/com/facebook/base/assets2/fonts/Theinhardt-Regular.otf",
                BIN_DIR
                    + "/java/src/com/facebook/base/__assets_apk__/fonts/Theinhardt-Regular.otf"));
    MoreAsserts.assertListEquals(expectedCommands, commands.build());
  }
  /**
   * Tests an android_binary with one dependent android_library rule that contains an assets
   * directory.
   */
  @Test
  public void testCreateAllAssetsDirectoryWithOneAssetsDirectory() throws IOException {
    BuildRuleResolver ruleResolver = new BuildRuleResolver();

    // Two android_library deps, one of which has an assets directory.
    JavaLibraryRule libraryOne =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryOne",
            ruleResolver,
            null, /* resDirectory */
            null, /* assetDirectory */
            null /* nativeLibsDirectory */);
    JavaLibraryRule libraryTwo =
        createAndroidLibraryRule(
            "//java/src/com/facebook/base:libraryTwo",
            ruleResolver,
            null, /* resDirectory */
            "java/src/com/facebook/base/assets2",
            null /* nativeLibsDirectory */);

    AndroidResourceRule resourceOne =
        (AndroidResourceRule)
            ruleResolver.get(
                BuildTargetFactory.newInstance(
                    "//java/src/com/facebook/base:libraryTwo_resources"));

    // One android_binary rule that depends on the two android_library rules.
    BuildTarget binaryBuildTarget =
        BuildTargetFactory.newInstance("//java/src/com/facebook/base:apk");
    AndroidBinaryRule androidBinary =
        ruleResolver.buildAndAddToIndex(
            AndroidBinaryRule.newAndroidBinaryRuleBuilder(new FakeAbstractBuildRuleBuilderParams())
                .setBuildTarget(binaryBuildTarget)
                .addClasspathDep(libraryOne.getBuildTarget())
                .addClasspathDep(libraryTwo.getBuildTarget())
                .setManifest("java/src/com/facebook/base/AndroidManifest.xml")
                .setTarget("Google Inc.:Google APIs:16")
                .setKeystore(addKeystoreRule(ruleResolver))
                .setPackageType("debug"));

    // Build up the parameters needed to invoke createAllAssetsDirectory().
    Set<String> assetsDirectories = ImmutableSet.of(resourceOne.getAssets());
    ImmutableList.Builder<Step> commands = ImmutableList.builder();
    DirectoryTraverser traverser =
        new DirectoryTraverser() {
          @Override
          public void traverse(DirectoryTraversal traversal) throws IOException {
            String rootPath = Paths.normalizePathSeparator(traversal.getRoot().getPath());
            if ("java/src/com/facebook/base/assets2".equals(rootPath)) {
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Medium.otf"),
                  "fonts/Theinhardt-Medium.otf");
              traversal.visit(
                  new File("java/src/com/facebook/base/assets2", "fonts/Theinhardt-Regular.otf"),
                  "fonts/Theinhardt-Regular.otf");
            } else {
              throw new RuntimeException("Unexpected path: " + rootPath);
            }
          }
        };

    // Invoke createAllAssetsDirectory(), the method under test.
    Optional<String> allAssetsDirectory =
        androidBinary.createAllAssetsDirectory(
            assetsDirectories, ImmutableMap.<String, File>of(), commands, traverser);

    // Verify that the existing assets/ directory will be passed to aapt.
    assertTrue(allAssetsDirectory.isPresent());
    assertEquals(
        "Even though there is only one assets directory, the one in "
            + BIN_DIR
            + " should be used.",
        androidBinary.getPathToAllAssetsDirectory(),
        allAssetsDirectory.get());
  }