Merge "Add tests for Proguard's behavior regarding -allowaccessmodification."
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 000076e..c098fb8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1413,6 +1413,11 @@
     return result.stdout;
   }
 
+  public static ProcessResult runProguardRaw(
+      Path inJar, Path outJar, Path lib, Path config, Path map) throws IOException {
+    return runProguardRaw(getProguardScript(), inJar, outJar, lib, ImmutableList.of(config), map);
+  }
+
   public static ProcessResult runProguardRaw(Path inJar, Path outJar, List<Path> config, Path map)
       throws IOException {
     return runProguardRaw(getProguardScript(), inJar, outJar, config, map);
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index c82bbc9..9a9ca34 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -4,52 +4,303 @@
 
 package com.android.tools.r8.naming.b72391662;
 
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.VmTestRunner;
 import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
 import com.android.tools.r8.naming.b72391662.subpackage.OtherPackageSuper;
 import com.android.tools.r8.naming.b72391662.subpackage.OtherPackageTestClass;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(VmTestRunner.class)
-public class B72391662 extends TestBase {
+public class B72391662 extends ProguardCompatabilityTestBase {
 
-  private void doTest(boolean allowAccessModification, boolean minify) throws Exception {
+  private static final List<Class> CLASSES = ImmutableList.of(
+      TestMain.class, Interface.class, Super.class, TestClass.class,
+      OtherPackageSuper.class, OtherPackageTestClass.class);
+
+  private void doTest_keepAll(
+      Shrinker shrinker,
+      String repackagePrefix,
+      boolean allowAccessModification,
+      boolean minify) throws Exception {
     Class mainClass = TestMain.class;
+    String keep = !minify ? "-keep" : "-keep,allowobfuscation";
     List<String> config = ImmutableList.of(
-        allowAccessModification ?"-allowaccessmodification" : "",
+        "-printmapping",
+        repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+        allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
         "  public void main(java.lang.String[]);",
         "}",
-        "-keep class " + TestClass.class.getCanonicalName() + " {",
+        keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  *;",
         "}",
-        "-keep class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+        keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
         "  *;",
-        "}"
+        "}",
+        "-dontwarn java.lang.invoke.*"
     );
 
-    AndroidApp app = readClassesAndAndriodJar(ImmutableList.of(
-        mainClass, Interface.class, Super.class, TestClass.class,
-        OtherPackageSuper.class, OtherPackageTestClass.class));
-    app = compileWithR8(app, String.join(System.lineSeparator(), config));
+    AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
     assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+    DexInspector dexInspector =
+        isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+    ClassSubject testClass = dexInspector.clazz(TestClass.class);
+    assertThat(testClass, isPresent());
+
+    // Test the totally unused method.
+    MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    if (isR8(shrinker)) {
+      assertEquals(allowAccessModification, staticMethod.getMethod().accessFlags.isPublic());
+    } else {
+      assertFalse(staticMethod.getMethod().accessFlags.isPublic());
+    }
+
+    // Test an indirectly referred method.
+    staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+        : minify && repackagePrefix != null && allowAccessModification;
+    assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
   }
 
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
-  public void test() throws Exception {
-    doTest(true, true);
-    doTest(true, false);
-    doTest(false, true);
-    doTest(false, false);
+  public void test_keepAll_R8() throws Exception {
+    doTest_keepAll(Shrinker.R8, "r8", true, true);
+    doTest_keepAll(Shrinker.R8, "r8", true, false);
+    doTest_keepAll(Shrinker.R8, "r8", false, true);
+    doTest_keepAll(Shrinker.R8, "r8", false, false);
+    doTest_keepAll(Shrinker.R8, null, true, true);
+    doTest_keepAll(Shrinker.R8, null, true, false);
+    doTest_keepAll(Shrinker.R8, null, false, true);
+    doTest_keepAll(Shrinker.R8, null, false, false);
+  }
+
+  @Ignore("b/92236970")
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepAll_R8Compat() throws Exception {
+    doTest_keepAll(Shrinker.R8_COMPAT, "rc", true, true);
+    doTest_keepAll(Shrinker.R8_COMPAT, "rc", true, false);
+    doTest_keepAll(Shrinker.R8_COMPAT, "rc", false, true);
+    doTest_keepAll(Shrinker.R8_COMPAT, "rc", false, false);
+    doTest_keepAll(Shrinker.R8_COMPAT, null, true, true);
+    doTest_keepAll(Shrinker.R8_COMPAT, null, true, false);
+    doTest_keepAll(Shrinker.R8_COMPAT, null, false, true);
+    doTest_keepAll(Shrinker.R8_COMPAT, null, false, false);
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepAll_Proguard6() throws Exception {
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+    doTest_keepAll(Shrinker.PROGUARD6_THEN_D8, null, false, false);
+  }
+
+  private void doTest_keepNonPublic(
+      Shrinker shrinker,
+      String repackagePrefix,
+      boolean allowAccessModification,
+      boolean minify) throws Exception {
+    Class mainClass = TestMain.class;
+    String keep = !minify ? "-keep" : "-keep,allowobfuscation";
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+        allowAccessModification ? "-allowaccessmodification" : "",
+        !minify ? "-dontobfuscate" : "",
+        "-keep class " + mainClass.getCanonicalName() + " {",
+        "  public void main(java.lang.String[]);",
+        "}",
+        keep + " class " + TestClass.class.getCanonicalName() + " {",
+        "  !public <methods>;",
+        "}",
+        keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+        "  !public <methods>;",
+        "}",
+        "-dontwarn java.lang.invoke.*"
+    );
+
+    AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
+    assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+    DexInspector dexInspector =
+        isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+    ClassSubject testClass = dexInspector.clazz(TestClass.class);
+    assertThat(testClass, isPresent());
+
+    // Test the totally unused method.
+    MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    if (isR8(shrinker)) {
+      assertEquals(allowAccessModification, staticMethod.getMethod().accessFlags.isPublic());
+    } else {
+      assertFalse(staticMethod.getMethod().accessFlags.isPublic());
+    }
+
+    // Test an indirectly referred method.
+    staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+        : minify && repackagePrefix != null && allowAccessModification;
+    assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepNonPublic_R8() throws Exception {
+    doTest_keepNonPublic(Shrinker.R8, "r8", true, true);
+    doTest_keepNonPublic(Shrinker.R8, "r8", true, false);
+    doTest_keepNonPublic(Shrinker.R8, "r8", false, true);
+    doTest_keepNonPublic(Shrinker.R8, "r8", false, false);
+    doTest_keepNonPublic(Shrinker.R8, null, true, true);
+    doTest_keepNonPublic(Shrinker.R8, null, true, false);
+    doTest_keepNonPublic(Shrinker.R8, null, false, true);
+    doTest_keepNonPublic(Shrinker.R8, null, false, false);
+  }
+
+  @Ignore("b/92236970")
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepNonPublic_R8Compat() throws Exception {
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", true, true);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", true, false);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", false, true);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, "rc", false, false);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, null, true, true);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, null, true, false);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, null, false, true);
+    doTest_keepNonPublic(Shrinker.R8_COMPAT, null, false, false);
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepNonPublic_Proguard6() throws Exception {
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+    doTest_keepNonPublic(Shrinker.PROGUARD6_THEN_D8, null, false, false);
+  }
+
+  private void doTest_keepPublic(
+      Shrinker shrinker,
+      String repackagePrefix,
+      boolean allowAccessModification,
+      boolean minify) throws Exception {
+    Class mainClass = TestMain.class;
+    String keep = !minify ? "-keep" : "-keep,allowobfuscation";
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
+        allowAccessModification ? "-allowaccessmodification" : "",
+        !minify ? "-dontobfuscate" : "",
+        "-keep class " + mainClass.getCanonicalName() + " {",
+        "  public void main(java.lang.String[]);",
+        "}",
+        keep + " class " + TestClass.class.getCanonicalName() + " {",
+        "  public <methods>;",
+        "}",
+        keep + " class " + OtherPackageTestClass.class.getCanonicalName() + " {",
+        "  public <methods>;",
+        "}",
+        "-dontwarn java.lang.invoke.*"
+    );
+
+    AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
+    assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
+
+    DexInspector dexInspector =
+        isR8(shrinker) ? new DexInspector(app) : new DexInspector(app, proguardMap);
+    ClassSubject testClass = dexInspector.clazz(TestClass.class);
+    assertThat(testClass, isPresent());
+
+    // Test the totally unused method.
+    MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+    assertThat(staticMethod, not(isPresent()));
+
+    // Test an indirectly referred method.
+    staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+    if (isR8(shrinker)) {
+      // Inlined.
+      assertThat(staticMethod, not(isPresent()));
+    } else {
+      assertThat(staticMethod, isPresent());
+      assertEquals(minify, staticMethod.isRenamed());
+      boolean publicizeCondition = minify && repackagePrefix != null && allowAccessModification;
+      assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
+    }
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepPublic_R8() throws Exception {
+    doTest_keepPublic(Shrinker.R8, "r8", true, true);
+    doTest_keepPublic(Shrinker.R8, "r8", true, false);
+    doTest_keepPublic(Shrinker.R8, "r8", false, true);
+    doTest_keepPublic(Shrinker.R8, "r8", false, false);
+    doTest_keepPublic(Shrinker.R8, null, true, true);
+    doTest_keepPublic(Shrinker.R8, null, true, false);
+    doTest_keepPublic(Shrinker.R8, null, false, true);
+    doTest_keepPublic(Shrinker.R8, null, false, false);
+  }
+
+  @Ignore("b/92236970")
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepPublic_R8Compat() throws Exception {
+    doTest_keepPublic(Shrinker.R8_COMPAT, "rc", true, true);
+    doTest_keepPublic(Shrinker.R8_COMPAT, "rc", true, false);
+    doTest_keepPublic(Shrinker.R8_COMPAT, "rc", false, true);
+    doTest_keepPublic(Shrinker.R8_COMPAT, "rc", false, false);
+    doTest_keepPublic(Shrinker.R8_COMPAT, null, true, true);
+    doTest_keepPublic(Shrinker.R8_COMPAT, null, true, false);
+    doTest_keepPublic(Shrinker.R8_COMPAT, null, false, true);
+    doTest_keepPublic(Shrinker.R8_COMPAT, null, false, false);
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void test_keepPublic_Proguard6() throws Exception {
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, true);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", true, false);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, true);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, "pg", false, false);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, true, true);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, true, false);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, false, true);
+    doTest_keepPublic(Shrinker.PROGUARD6_THEN_D8, null, false, false);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java b/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
index 88efc09..f13700c 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/TestClass.java
@@ -18,6 +18,10 @@
     return value;
   }
 
+  static void unused() {
+    System.out.println("This should be discarded unless there is a keep rule.");
+  }
+
   static String staticMethod() {
     return "1";
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 3976f89..e29ceae 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -6,13 +6,16 @@
 import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.CompatProguardCommandBuilder;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -24,6 +27,8 @@
 
 public class ProguardCompatabilityTestBase extends TestBase {
 
+  protected Path proguardMap;
+
   public enum Shrinker {
     PROGUARD5,
     PROGUARD6,
@@ -32,6 +37,34 @@
     R8
   }
 
+  protected static boolean isR8(Shrinker shrinker) {
+    return shrinker == Shrinker.R8_COMPAT || shrinker == Shrinker.R8;
+  }
+
+  protected AndroidApp runShrinkerRaw(
+      Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
+    return runShrinkerRaw(
+        mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
+  }
+
+  protected AndroidApp runShrinkerRaw(
+      Shrinker mode, List<Class> programClasses, String proguardConfig) throws Exception {
+    proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+    switch (mode) {
+      case PROGUARD5:
+        return runProguard5Raw(programClasses, proguardConfig, proguardMap);
+      case PROGUARD6:
+        return runProguard6Raw(programClasses, proguardConfig, proguardMap);
+      case PROGUARD6_THEN_D8:
+        return runProguard6AndD8Raw(programClasses, proguardConfig, proguardMap);
+      case R8_COMPAT:
+        return runR8CompatRaw(programClasses, proguardConfig);
+      case R8:
+        return runR8Raw(programClasses, proguardConfig);
+    }
+    throw new IllegalArgumentException("Unknown shrinker: " + mode);
+  }
+
   protected DexInspector runShrinker(
       Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
     return runShrinker(mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
@@ -54,21 +87,31 @@
     throw new IllegalArgumentException("Unknown shrinker: " + mode);
   }
 
-  protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
-    AndroidApp app = readClasses(programClasses);
+  protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig) throws Exception {
+    AndroidApp app = readClassesAndAndriodJar(programClasses);
     R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app);
     builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
-    return new DexInspector(ToolHelper.runR8(builder.build()));
+    return ToolHelper.runR8(builder.build());
   }
 
-  protected DexInspector runR8Compat(
+  protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
+    return new DexInspector(runR8Raw(programClasses, proguardConfig));
+  }
+
+  protected AndroidApp runR8CompatRaw(
       List<Class> programClasses, String proguardConfig) throws Exception {
     CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
     builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
     programClasses.forEach(
         clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
+    builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
     builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-    return new DexInspector(ToolHelper.runR8(builder.build()));
+    return ToolHelper.runR8(builder.build());
+  }
+
+  protected DexInspector runR8Compat(
+      List<Class> programClasses, String proguardConfig) throws Exception {
+    return new DexInspector(runR8CompatRaw(programClasses, proguardConfig));
   }
 
   protected DexInspector runR8CompatKeepingMain(Class mainClass, List<Class> programClasses)
@@ -76,41 +119,79 @@
     return runR8Compat(programClasses, keepMainProguardConfiguration(mainClass));
   }
 
-  protected DexInspector runProguard5(
-      List<Class> programClasses, String proguardConfig) throws Exception {
+  protected AndroidApp runProguard5Raw(
+      List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
     Path proguardedJar =
         File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
     Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
-    Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
     FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
-    ToolHelper.runProguard(
-        jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
-    return new DexInspector(readJar(proguardedJar), proguardMap);
+    ProcessResult result = ToolHelper.runProguardRaw(
+        jarTestClasses(programClasses),
+        proguardedJar,
+        ToolHelper.getAndroidJar(AndroidApiLevel.N),
+        proguardConfigFile,
+        proguardMap);
+    if (result.exitCode != 0) {
+      fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+    return readJar(proguardedJar);
+  }
+
+  protected DexInspector runProguard5(
+      List<Class> programClasses, String proguardConfig) throws Exception {
+    proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+    return new DexInspector(
+        runProguard5Raw(programClasses, proguardConfig, proguardMap), proguardMap);
+  }
+
+  protected AndroidApp runProguard6Raw(
+      List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
+    Path proguardedJar =
+        File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
+    Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
+    FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
+    ProcessResult result = ToolHelper.runProguard6Raw(
+        jarTestClasses(programClasses),
+        proguardedJar,
+        ToolHelper.getAndroidJar(AndroidApiLevel.N),
+        proguardConfigFile,
+        proguardMap);
+    if (result.exitCode != 0) {
+      fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+    return readJar(proguardedJar);
   }
 
   protected DexInspector runProguard6(
       List<Class> programClasses, String proguardConfig) throws Exception {
+    proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+    return new DexInspector(
+        runProguard6Raw(programClasses, proguardConfig, proguardMap), proguardMap);
+  }
+
+  protected AndroidApp runProguard6AndD8Raw(
+      List<Class> programClasses, String proguardConfig, Path proguardMap) throws Exception {
     Path proguardedJar =
         File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
     Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
-    Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
     FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
-    ToolHelper.runProguard6(
-        jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
-    return new DexInspector(readJar(proguardedJar), proguardMap);
+    ProcessResult result = ToolHelper.runProguard6Raw(
+        jarTestClasses(programClasses),
+        proguardedJar,
+        ToolHelper.getAndroidJar(AndroidApiLevel.N),
+        proguardConfigFile,
+        proguardMap);
+    if (result.exitCode != 0) {
+      fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+    return ToolHelper.runD8(readJar(proguardedJar));
   }
 
   protected DexInspector runProguard6AndD8(
       List<Class> programClasses, String proguardConfig) throws Exception {
-    Path proguardedJar =
-        File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
-    Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
-    Path proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
-    FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
-    ToolHelper.runProguard6(
-        jarTestClasses(programClasses), proguardedJar, proguardConfigFile, proguardMap);
-    AndroidApp app = ToolHelper.runD8(readJar(proguardedJar));
-    return new DexInspector(app, proguardMap);
+    proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
+    return new DexInspector(
+        runProguard6AndD8Raw(programClasses, proguardConfig, proguardMap), proguardMap);
   }
 
   protected DexInspector runProguardKeepingMain(Class mainClass, List<Class> programClasses)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
new file mode 100644
index 0000000..65d1763
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -0,0 +1,195 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IfOnAccessModifierTest extends ProguardCompatabilityTestBase {
+    private final static List<Class> CLASSES = ImmutableList.of(
+        ClassForIf.class, ClassForSubsequent.class,
+        MainForAccessModifierTest.class);
+
+  private final Shrinker shrinker;
+  private final MethodSignature nonPublicMethod;
+  private final MethodSignature publicMethod;
+
+  public IfOnAccessModifierTest(Shrinker shrinker) {
+    this.shrinker = shrinker;
+    nonPublicMethod = new MethodSignature("nonPublicMethod", "void", ImmutableList.of());
+    publicMethod = new MethodSignature("publicMethod", "void", ImmutableList.of());
+  }
+
+  @Parameters(name = "shrinker: {0}")
+  public static Collection<Object> data() {
+    return ImmutableList.of(Shrinker.PROGUARD6, Shrinker.R8);
+  }
+
+  @Test
+  public void ifOnNonPublic_keepOnPublic() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-repackageclasses 'top'",
+        "-allowaccessmodification",
+        "-keep class **.Main* {",
+        "  public static void callIfNonPublic();",
+        "}",
+        "-if class **.ClassForIf {",
+        "  !public <methods>;",
+        "}",
+        "-keep,allowobfuscation class **.ClassForSubsequent {",
+        "  public <methods>;",
+        "}"
+    );
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, not(isPresent()));
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, isPresent());
+    assertTrue(methodSubject.getMethod().accessFlags.isPublic());
+
+    classSubject = dexInspector.clazz(ClassForSubsequent.class);
+    if (isR8(shrinker)) {
+      // TODO(b/72109068): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
+      // at the 2nd tree shaking.
+      assertThat(classSubject, not(isPresent()));
+      return;
+    }
+    assertThat(classSubject, isPresent());
+    methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, isPresent());
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, not(isPresent()));
+  }
+
+  @Test
+  public void ifOnNonPublic_keepOnNonPublic() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-repackageclasses 'top'",
+        "-allowaccessmodification",
+        "-keep class **.Main* {",
+        "  public static void callIfNonPublic();",
+        "}",
+        "-if class **.ClassForIf {",
+        "  !public <methods>;",
+        "}",
+        "-keep,allowobfuscation class **.ClassForSubsequent {",
+        "  !public <methods>;",
+        "}"
+    );
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, not(isPresent()));
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, isPresent());
+    assertTrue(methodSubject.getMethod().accessFlags.isPublic());
+
+    classSubject = dexInspector.clazz(ClassForSubsequent.class);
+    if (isR8(shrinker)) {
+      // TODO(b/72109068): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
+      // at the 2nd tree shaking.
+      assertThat(classSubject, not(isPresent()));
+      return;
+    }
+    assertThat(classSubject, isPresent());
+    methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, not(isPresent()));
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, isPresent());
+    assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+  }
+
+  @Test
+  public void ifOnPublic_keepOnPublic() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-repackageclasses 'top'",
+        "-allowaccessmodification",
+        "-keep class **.Main* {",
+        "  public static void callIfPublic();",
+        "}",
+        "-if class **.ClassForIf {",
+        "  public <methods>;",
+        "}",
+        "-keep,allowobfuscation class **.ClassForSubsequent {",
+        "  public <methods>;",
+        "}"
+    );
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, isPresent());
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, not(isPresent()));
+
+    classSubject = dexInspector.clazz(ClassForSubsequent.class);
+    assertThat(classSubject, isPresent());
+    methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, isPresent());
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, not(isPresent()));
+  }
+
+  @Test
+  public void ifOnPublic_keepOnNonPublic() throws Exception {
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-repackageclasses 'top'",
+        "-allowaccessmodification",
+        "-keep class **.Main* {",
+        "  public static void callIfPublic();",
+        "}",
+        "-if class **.ClassForIf {",
+        "  public <methods>;",
+        "}",
+        "-keep,allowobfuscation class **.ClassForSubsequent {",
+        "  !public <methods>;",
+        "}"
+    );
+    DexInspector dexInspector = runShrinker(shrinker, CLASSES, config);
+    ClassSubject classSubject = dexInspector.clazz(ClassForIf.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, isPresent());
+    methodSubject = classSubject.method(nonPublicMethod);
+    assertThat(methodSubject, not(isPresent()));
+
+    classSubject = dexInspector.clazz(ClassForSubsequent.class);
+    assertThat(classSubject, isPresent());
+    methodSubject = classSubject.method(publicMethod);
+    assertThat(methodSubject, not(isPresent()));
+    methodSubject = classSubject.method(nonPublicMethod);
+    if (isR8(shrinker)) {
+      // TODO(b/72109068): if kept in the 1st tree shaking, should be kept after publicizing.
+      assertThat(methodSubject, not(isPresent()));
+      return;
+    }
+    assertThat(methodSubject, isPresent());
+    assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java
new file mode 100644
index 0000000..1b5bd75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTestClasses.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+class ClassForIf {
+  ClassForIf() {
+  }
+
+  synchronized void nonPublicMethod() {
+    System.out.println("ClassForIf::nonPublicMethod");
+  }
+
+  synchronized public void publicMethod() {
+    System.out.println("ClassForIf::publicMethod");
+  }
+}
+
+class ClassForSubsequent {
+  ClassForSubsequent() {
+  }
+
+  synchronized void nonPublicMethod() {
+    System.out.println("ClassForSubsequent::nonPublicMethod");
+  }
+
+  synchronized public void publicMethod() {
+    System.out.println("ClassForSubsequent::publicMethod");
+  }
+}
+
+class MainForAccessModifierTest {
+  public static void callIfNonPublic() {
+    new ClassForIf().nonPublicMethod();
+  }
+
+  public static void callIfPublic() {
+    new ClassForIf().publicMethod();
+  }
+}