@Test
  public void testAllExopackageHasNeitherSecondaryNorNativeLibraries() throws IOException {
    ZipInspector zipInspector =
        new ZipInspector(
            workspace.getPath(
                BuildTargets.getGenPath(
                    filesystem,
                    BuildTargetFactory.newInstance(DEX_AND_NATIVE_EXOPACKAGE_TARGET),
                    "%s.apk")));

    zipInspector.assertFileDoesNotExist("assets/secondary-program-dex-jars/metadata.txt");
    zipInspector.assertFileDoesNotExist("classes2.dex");

    zipInspector.assertFileExists("classes.dex");

    assertNativeLibrariesDontExist(zipInspector);
  }
  @Test
  public void testDexExopackageHasNoSecondary() throws IOException {
    ZipInspector zipInspector =
        new ZipInspector(
            workspace.getPath(
                BuildTargets.getGenPath(
                    filesystem, BuildTargetFactory.newInstance(DEX_EXOPACKAGE_TARGET), "%s.apk")));
    zipInspector.assertFileDoesNotExist("assets/secondary-program-dex-jars/metadata.txt");
    zipInspector.assertFileDoesNotExist("assets/secondary-program-dex-jars/secondary-1.dex.jar");
    zipInspector.assertFileDoesNotExist("classes2.dex");

    zipInspector.assertFileExists("classes.dex");
    zipInspector.assertFileExists("lib/armeabi/libfakenative.so");

    // It would be better if we could call getExopackageInfo on the app rule.
    Path secondaryDir =
        workspace.resolve(
            BuildTargets.getScratchPath(
                filesystem,
                BuildTargetFactory.newInstance(DEX_EXOPACKAGE_TARGET)
                    .withFlavors(ImmutableFlavor.of("dex_merge")),
                "_%s_output/jarfiles/assets/secondary-program-dex-jars"));

    try (DirectoryStream<Path> stream = Files.newDirectoryStream(secondaryDir)) {
      List<Path> files = Lists.newArrayList(stream);
      assertEquals(2, files.size());
      Collections.sort(files);

      Path secondaryJar = files.get(0);
      ZipInspector zi = new ZipInspector(secondaryJar);
      zi.assertFileExists("classes.dex");
      long jarSize = Files.size(secondaryJar);
      long classesDexSize = zi.getSize("classes.dex");

      Path dexMeta = files.get(1);
      assertEquals(
          String.format("jar:%s dex:%s", jarSize, classesDexSize),
          new String(Files.readAllBytes(dexMeta), "US-ASCII"));
    }
  }
  @Test
  public void testNativeExopackageHasNoNativeLibraries() throws IOException {
    ZipInspector zipInspector =
        new ZipInspector(workspace.getPath("buck-out/gen/apps/multidex/app-native-exo.apk"));

    zipInspector.assertFileDoesNotExist("assets/secondary-program-dex-jars/metadata.txt");

    zipInspector.assertFileExists("classes2.dex");

    zipInspector.assertFileExists("classes.dex");

    assertNativeLibrariesDontExist(zipInspector);
  }
  @Test
  public void testEditingNativeForcesRebuild() throws IOException, InterruptedException {
    // Sleep 1 second (plus another half to be super duper safe) to make sure that
    // fakesystem.c gets a later timestamp than the fakesystem.o that was produced
    // during the build in setUp.  If we don't do this, there's a chance that the
    // ndk-build we run during the upcoming build will not rebuild it (on filesystems
    // that have 1-second granularity for last modified).
    // To verify this, create a Makefile with the following rule (don't forget to use a tab):
    // out: in
    //   cat $< > $@
    // Run: echo foo > in ; make ; cat out ; echo bar > in ; make ; cat out
    // On a filesystem with 1-second mtime granularity, the last "cat" should print "foo"
    // (with very high probability).
    Thread.sleep(1500);

    ZipInspector zipInspector;

    // Change the binary and ensure that we re-run apkbuilder.
    workspace.replaceFileContents(
        "native/fakenative/jni/fakesystem.c", "exit(status)", "exit(1+status)");

    workspace.resetBuildLogFile();
    workspace.runBuckBuild(DEX_EXOPACKAGE_TARGET).assertSuccess();

    workspace.getBuildLog().assertTargetBuiltLocally(DEX_EXOPACKAGE_TARGET);
    zipInspector =
        new ZipInspector(workspace.getPath("buck-out/gen/apps/multidex/app-dex-exo.apk"));
    zipInspector.assertFileExists("lib/armeabi/libfakenative.so");
    zipInspector.assertFileDoesNotExist("assets/lib/armeabi/libfakenative.so");

    // Now convert it into an asset native library and ensure that we re-run apkbuilder.
    workspace.replaceFileContents(
        "native/fakenative/jni/BUCK",
        "name = 'fakenative',",
        "name = 'fakenative',\nis_asset=True,");

    workspace.resetBuildLogFile();
    workspace.runBuckBuild(DEX_EXOPACKAGE_TARGET).assertSuccess();

    workspace.getBuildLog().assertTargetBuiltLocally(DEX_EXOPACKAGE_TARGET);
    zipInspector =
        new ZipInspector(workspace.getPath("buck-out/gen/apps/multidex/app-dex-exo.apk"));
    zipInspector.assertFileDoesNotExist("lib/armeabi/libfakenative.so");
    zipInspector.assertFileExists("assets/lib/armeabi/libfakenative.so");

    // Now edit it again and make sure we re-run apkbuilder.
    Thread.sleep(1500);

    workspace.replaceFileContents(
        "native/fakenative/jni/fakesystem.c", "exit(1+status)", "exit(2+status)");

    workspace.resetBuildLogFile();
    workspace.runBuckBuild(DEX_EXOPACKAGE_TARGET).assertSuccess();

    workspace.getBuildLog().assertTargetBuiltLocally(DEX_EXOPACKAGE_TARGET);
    zipInspector =
        new ZipInspector(
            workspace.getPath(
                BuildTargets.getGenPath(
                    filesystem, BuildTargetFactory.newInstance(DEX_EXOPACKAGE_TARGET), "%s.apk")));
    zipInspector.assertFileDoesNotExist("lib/armeabi/libfakenative.so");
    zipInspector.assertFileExists("assets/lib/armeabi/libfakenative.so");
  }