Rewrite DefaultMethodsTest to new test infrastructure

Change-Id: Ib6ad7fdf3eb92b855ca540b44e1e96d23569c37e
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index eebb7d5..690052b 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.shaking.defaultmethods;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ClassFileConsumer;
-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.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -24,47 +22,33 @@
 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 DefaultMethodsTest extends TestBase {
 
-  private Backend backend;
+  private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "Backend: {0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public DefaultMethodsTest(Backend backend) {
-    this.backend = backend;
+  public DefaultMethodsTest(TestParameters parameters) {
+    this.parameters = parameters;
   }
 
   private void runTest(List<String> additionalKeepRules, Consumer<CodeInspector> inspection)
       throws Exception {
-    R8Command.Builder builder = R8Command.builder();
-    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(InterfaceWithDefaultMethods.class));
-    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(ClassImplementingInterface.class));
-    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class));
-    if (backend == Backend.DEX) {
-      builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
-      int apiLevel = AndroidApiLevel.O.getLevel();
-      builder.setMinApiLevel(apiLevel);
-      builder.addLibraryFiles(ToolHelper.getAndroidJar(apiLevel));
-    } else {
-      assert backend == Backend.CF;
-      builder.setProgramConsumer(ClassFileConsumer.emptyConsumer());
-      builder.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
-    }
-    // Always keep main in the test class, so the output never becomes empty.
-    builder.addProguardConfiguration(ImmutableList.of(
-        "-keep class " + TestClass.class.getCanonicalName() + "{",
-        "  public static void main(java.lang.String[]);",
-        "}",
-        "-dontobfuscate"),
-        Origin.unknown());
-    builder.addProguardConfiguration(additionalKeepRules, Origin.unknown());
-    AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
-    inspection.accept(new CodeInspector(app));
+    testForR8(parameters.getBackend())
+        .addProgramClasses(
+            InterfaceWithDefaultMethods.class, ClassImplementingInterface.class, TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(additionalKeepRules)
+        .noMinification()
+        .compile()
+        .inspect(inspection);
   }
 
   private void interfaceNotKept(CodeInspector inspector) {
@@ -73,53 +57,98 @@
 
   private void defaultMethodNotKept(CodeInspector inspector) {
     ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
-    assertTrue(clazz.isPresent());
-    assertFalse(clazz.method("int", "method", ImmutableList.of()).isPresent());
+    assertThat(clazz, isPresent());
+    assertThat(clazz.method("int", "method", ImmutableList.of()), not(isPresent()));
   }
 
   private void defaultMethodKept(CodeInspector inspector) {
     ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
-    assertTrue(clazz.isPresent());
+    assertThat(clazz, isPresent());
     MethodSubject method = clazz.method("int", "method", ImmutableList.of());
-    assertTrue(method.isPresent());
-    assertFalse(method.isAbstract());
+    assertThat(method, isPresent());
+    ClassSubject companionClass = clazz.toCompanionClass();
+    if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+      assertThat(method, not(isAbstract()));
+      assertThat(companionClass, not(isPresent()));
+    } else {
+      assertThat(method, isAbstract());
+      assertThat(companionClass, isPresent());
+      MethodSubject defaultMethod = method.toMethodOnCompanionClass();
+      assertThat(defaultMethod, isPresent());
+    }
+  }
+
+  private void defaultMethodKeptWithoutCompanionClass(CodeInspector inspector) {
+    ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
+    assertThat(clazz, isPresent());
+    MethodSubject method = clazz.method("int", "method", ImmutableList.of());
+    assertThat(method, isPresent());
+    ClassSubject companionClass = clazz.toCompanionClass();
+    if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+      assertThat(method, not(isAbstract()));
+    } else {
+      assertThat(method, isAbstract());
+    }
+    assertThat(companionClass, not(isPresent()));
   }
 
   @Test
-  public void test() throws Exception {
+  public void testInterfaceNotKept() throws Exception {
     runTest(ImmutableList.of(), this::interfaceNotKept);
-    runTest(ImmutableList.of(
-        "-keep interface " + InterfaceWithDefaultMethods.class.getCanonicalName() + "{",
-        "}"
-    ), this::defaultMethodNotKept);
-    runTest(ImmutableList.of(
-        "-keep interface " + InterfaceWithDefaultMethods.class.getCanonicalName() + "{",
-        "  <methods>;",
-        "}"
-    ), this::defaultMethodKept);
-    runTest(ImmutableList.of(
-        "-keep interface " + InterfaceWithDefaultMethods.class.getCanonicalName() + "{",
-        "  public int method();",
-        "}"
-    ), this::defaultMethodKept);
+  }
+
+  @Test
+  public void testDefaultMethodNotKept() throws Exception {
     runTest(
         ImmutableList.of(
-            "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+            "-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{", "}"),
+        this::defaultMethodNotKept);
+  }
+
+  @Test
+  public void testDefaultMethodKeptWithMethods() throws Exception {
+    runTest(
+        ImmutableList.of(
+            "-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
+            "  <methods>;",
+            "}"),
+        this::defaultMethodKept);
+  }
+
+  @Test
+  public void testDefaultMethodsKeptExplicitly() throws Exception {
+    runTest(
+        ImmutableList.of(
+            "-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
+            "  public int method();",
+            "}"),
+        this::defaultMethodKept);
+  }
+
+  @Test
+  public void testDefaultMethodNotKeptIndirectly() throws Exception {
+    runTest(
+        ImmutableList.of(
+            "-keep class " + ClassImplementingInterface.class.getTypeName() + "{",
             "  <methods>;",
             "}",
             // Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
-            "-keep class " + InterfaceWithDefaultMethods.class.getCanonicalName()),
+            "-keep class " + InterfaceWithDefaultMethods.class.getTypeName()),
         this::defaultMethodNotKept);
+  }
+
+  @Test
+  public void testDefaultMethodKeptIndirectly() throws Exception {
     runTest(
         ImmutableList.of(
-            "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+            "-keep class " + ClassImplementingInterface.class.getTypeName() + "{",
             "  <methods>;",
             "}",
             "-keep class " + TestClass.class.getCanonicalName() + "{",
             "  public void useInterfaceMethod();",
             "}",
             // Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
-            "-keep class " + InterfaceWithDefaultMethods.class.getCanonicalName()),
-        this::defaultMethodKept);
+            "-keep class " + InterfaceWithDefaultMethods.class.getTypeName()),
+        this::defaultMethodKeptWithoutCompanionClass);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 81b988e..f872a70 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -7,12 +7,17 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.references.ClassReference;
 import java.util.List;
 import java.util.function.Consumer;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public class AbsentClassSubject extends ClassSubject {
 
+  public AbsentClassSubject(CodeInspector codeInspector, ClassReference reference) {
+    super(codeInspector, reference);
+  }
+
   @Override
   public boolean isPresent() {
     return false;
@@ -172,4 +177,9 @@
   public KotlinClassMetadata getKotlinClassMetadata() {
     return null;
   }
+
+  @Override
+  public ClassSubject toCompanionClass() {
+    throw new Unreachable("Cannot determine EnclosingMethod attribute of an absent class");
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 38dc236..c62f945 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -142,4 +142,9 @@
   public String getJvmMethodSignatureAsString() {
     return null;
   }
+
+  @Override
+  public MethodSubject toMethodOnCompanionClass() {
+    throw new Unreachable("Cannot determine companion class method");
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 97a3b29..84d76a1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -4,10 +4,14 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.utils.ListUtils;
@@ -23,6 +27,14 @@
 
 public abstract class ClassSubject extends Subject {
 
+  protected final ClassReference reference;
+  protected final CodeInspector codeInspector;
+
+  public ClassSubject(CodeInspector codeInspector, ClassReference reference) {
+    this.codeInspector = codeInspector;
+    this.reference = reference;
+  }
+
   public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
 
   public final List<FoundMethodSubject> allMethods() {
@@ -192,4 +204,11 @@
   public abstract KmPackageSubject getKmPackage();
 
   public abstract KotlinClassMetadata getKotlinClassMetadata();
+
+  public ClassSubject toCompanionClass() {
+    String descriptor = reference.getDescriptor();
+    return codeInspector.clazz(
+        Reference.classFromDescriptor(
+            descriptor.substring(0, descriptor.length() - 1) + COMPANION_CLASS_NAME_SUFFIX + ";"));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 76ed0e2..ac30cb7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -280,9 +280,9 @@
     }
     DexClass clazz = application.definitionFor(toDexTypeIgnorePrimitives(name));
     if (clazz == null) {
-      return new AbsentClassSubject();
+      return new AbsentClassSubject(this, reference);
     }
-    return new FoundClassSubject(this, clazz, naming);
+    return new FoundClassSubject(this, clazz, naming, reference);
   }
 
   public ClassSubject companionClassFor(Class<?> clazz) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 2ce3024..bd65812 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.List;
@@ -32,13 +33,15 @@
 
 public class FoundClassSubject extends ClassSubject {
 
-  private final CodeInspector codeInspector;
   private final DexClass dexClass;
   final ClassNamingForNameMapper naming;
 
   FoundClassSubject(
-      CodeInspector codeInspector, DexClass dexClass, ClassNamingForNameMapper naming) {
-    this.codeInspector = codeInspector;
+      CodeInspector codeInspector,
+      DexClass dexClass,
+      ClassNamingForNameMapper naming,
+      ClassReference reference) {
+    super(codeInspector, reference);
     this.dexClass = dexClass;
     this.naming = naming;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
index 6363747..a74c3d8 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
@@ -129,7 +129,7 @@
   default ClassSubject getClassSubjectFromKmType(KmType kmType) {
     String descriptor = getDescriptorFromKmType(kmType);
     if (descriptor == null) {
-      return new AbsentClassSubject();
+      return new AbsentClassSubject(codeInspector(), Reference.classFromDescriptor("Lnot_found;"));
     }
     return getClassSubjectFromDescriptor(descriptor);
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 1f1fe84..d4f8b5b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX;
+
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.code.Instruction;
@@ -31,6 +33,7 @@
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
 import com.google.common.base.Predicates;
@@ -39,6 +42,7 @@
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -372,4 +376,19 @@
         + ")"
         + dexMethod.method.proto.returnType.toDescriptorString();
   }
+
+  @Override
+  public MethodSubject toMethodOnCompanionClass() {
+    ClassSubject companionClass = clazz.toCompanionClass();
+    MethodReference reference = asMethodReference();
+    List<String> p =
+        ImmutableList.<String>builder()
+            .add(clazz.getFinalName())
+            .addAll(reference.getFormalTypes().stream().map(TypeReference::getTypeName).iterator())
+            .build();
+    return companionClass.method(
+        reference.getReturnType().getTypeName(),
+        DEFAULT_METHOD_PREFIX + reference.getMethodName(),
+        p);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 8fc439f..690838d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -102,4 +102,6 @@
   }
 
   public abstract String getJvmMethodSignatureAsString();
+
+  public abstract MethodSubject toMethodOnCompanionClass();
 }