Reproduction of trace references missing keep rule on default methods

BUG=b/319190998

Change-Id: I8983616c4aae2f681b42ac510766259777f6af90
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 1792e97..fc90c26 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -280,7 +280,7 @@
         "No need to use is/assumeR8TestParameters() when not using api levels for CF",
         isCfRuntime() && apiLevel == null);
     assertTrue(apiLevel != null || representativeApiLevelForRuntime);
-    return isDexRuntime() || representativeApiLevelForRuntime;
+    return (isDexRuntime() || representativeApiLevelForRuntime) && !isNoneRuntime();
   }
 
   public TestParameters assumeRuntimeTestParameters() {
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 9b4e4fd..2c07a9a 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
 import com.android.tools.r8.references.ClassReference;
@@ -480,6 +482,10 @@
 
   public abstract T addApplyMapping(String proguardMap);
 
+  public T addApplyMapping(Path proguardMap) throws IOException {
+    return addApplyMapping(FileUtils.readTextFile(proguardMap, UTF_8));
+  }
+
   public final T addAlwaysClassInlineAnnotation() {
     return addTestingAnnotation(AlwaysClassInline.class);
   }
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java
new file mode 100644
index 0000000..95ef480
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java
@@ -0,0 +1,320 @@
+// Copyright (c) 2024, 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.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// From b/319190998.
+@RunWith(Parameterized.class)
+public class TraceReferencesDefaultMethodInSubInterfaceTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withAllRuntimesAndApiLevels()
+        .withAllApiLevelsAlsoForCf()
+        .withNoneRuntime()
+        .build();
+  }
+
+  static Path targetJar;
+  static Path sourceJar;
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    Path dir = getStaticTemp().newFolder().toPath();
+    targetJar =
+        ZipBuilder.builder(dir.resolve("target.jar"))
+            .addFilesRelative(
+                ToolHelper.getClassPathForTests(),
+                ToolHelper.getClassFileForTestClass(I.class),
+                ToolHelper.getClassFileForTestClass(J.class))
+            .build();
+    sourceJar =
+        ZipBuilder.builder(dir.resolve("source.jar"))
+            .addFilesRelative(
+                ToolHelper.getClassPathForTests(),
+                ToolHelper.getClassFileForTestClass(JImpl.class),
+                ToolHelper.getClassFileForTestClass(Main.class))
+            .build();
+  }
+
+  static class SeenReferencesConsumer implements TraceReferencesConsumer {
+
+    private final Set<MethodReference> seenMethods = new HashSet<>();
+
+    @Override
+    public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+    @Override
+    public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {}
+
+    @Override
+    public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+      seenMethods.add(tracedMethod.getReference());
+    }
+  }
+
+  @Test
+  public void testTracedReferences() throws Exception {
+    assumeTrue(parameters.isNoneRuntime());
+    SeenReferencesConsumer consumer = new SeenReferencesConsumer();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addSourceFiles(sourceJar)
+            .addTargetFiles(targetJar)
+            .setConsumer(consumer)
+            .build());
+
+    // TODO(b/319190998): Just tracing I.m is not enough.
+    ImmutableSet<MethodReference> expectedSet =
+        ImmutableSet.of(
+            Reference.method(
+                Reference.classFromClass(I.class),
+                "m",
+                Collections.emptyList(),
+                Reference.classFromClass(Object.class)));
+    assertEquals(expectedSet, consumer.seenMethods);
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addProgramFiles(sourceJar)
+        .addProgramFiles(targetJar)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    Path targetDex =
+        testForD8().setMinApi(parameters).addProgramFiles(targetJar).compile().writeToZip();
+
+    testForD8()
+        .setMinApi(parameters)
+        .addClasspathFiles(targetJar)
+        .addProgramFiles(sourceJar)
+        .addRunClasspathFiles(targetDex)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testGeneratedKeepRulesFollowedByR8() throws Exception {
+    parameters.assumeR8TestParameters();
+
+    Path generatedKeepRules = temp.newFile("keep.rules").toPath();
+    TraceReferencesKeepRules keepRulesConsumer =
+        TraceReferencesKeepRules.builder()
+            // The use of keeper in b/319190998 disables obfuscation of generated keep rules.
+            .setAllowObfuscation(false)
+            .setOutputPath(generatedKeepRules)
+            .build();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addSourceFiles(sourceJar)
+            .addTargetFiles(targetJar)
+            .setConsumer(keepRulesConsumer)
+            .build());
+
+    Path r8CompiledTarget =
+        testForR8(parameters.getBackend())
+            .setMinApi(parameters)
+            .addProgramFiles(targetJar)
+            .addKeepRuleFiles(generatedKeepRules)
+            .compile()
+            .writeToZip();
+
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addClasspathFiles(targetJar)
+        .addProgramFiles(sourceJar)
+        .addRunClasspathFiles(r8CompiledTarget)
+        .addKeepMainRule(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/319190998): This should not fail.
+        .applyIf(
+            hasDefaultInterfaceMethodsSupport(parameters),
+            r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class),
+            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+  }
+
+  @Test
+  public void testGeneratedKeepRulesWithMissingRuleFollowedByR8() throws Exception {
+    parameters.assumeR8TestParameters();
+
+    Path generatedKeepRules = temp.newFile("keep.rules").toPath();
+    TraceReferencesKeepRules keepRulesConsumer =
+        TraceReferencesKeepRules.builder()
+            .setAllowObfuscation(true)
+            .setOutputPath(generatedKeepRules)
+            .build();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addSourceFiles(sourceJar)
+            .addTargetFiles(targetJar)
+            .setConsumer(keepRulesConsumer)
+            .build());
+
+    Path proguardMap = temp.newFolder().toPath().resolve("mapping.txt");
+    Path r8CompiledTarget =
+        testForR8(parameters.getBackend())
+            .setMinApi(parameters)
+            .addProgramFiles(targetJar)
+            .addKeepRuleFiles(generatedKeepRules)
+            .addKeepRules("-keep class " + J.class.getTypeName() + " { m(); }")
+            .compile()
+            .apply(r -> r.writeProguardMap(proguardMap))
+            .writeToZip();
+
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters)
+        .addClasspathFiles(targetJar)
+        .addProgramFiles(sourceJar)
+        .addApplyMapping(proguardMap)
+        .addKeepMainRule(Main.class)
+        .addRunClasspathFiles(r8CompiledTarget)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testGeneratedKeepRulesWithMissingRuleFollowedByD8() throws Exception {
+    parameters.assumeDexRuntime();
+
+    Path generatedKeepRules = temp.newFile("keep.rules").toPath();
+    TraceReferencesKeepRules keepRulesConsumer =
+        TraceReferencesKeepRules.builder()
+            // Don't obfuscate as D8 does not support apply mapping.
+            .setAllowObfuscation(false)
+            .setOutputPath(generatedKeepRules)
+            .build();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addSourceFiles(sourceJar)
+            .addTargetFiles(targetJar)
+            .setConsumer(keepRulesConsumer)
+            .build());
+
+    Path r8CompiledTarget =
+        testForR8(Backend.DEX)
+            .setMinApi(parameters)
+            .addProgramFiles(targetJar)
+            .addKeepRuleFiles(generatedKeepRules)
+            .addKeepRules("-keep class " + J.class.getTypeName() + " { m(); }")
+            .compile()
+            .writeToZip();
+
+    testForD8(Backend.DEX)
+        .setMinApi(parameters)
+        .addClasspathFiles(targetJar)
+        .addProgramFiles(sourceJar)
+        .addRunClasspathFiles(r8CompiledTarget)
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            hasDefaultInterfaceMethodsSupport(parameters),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            // TODO(b/319190998): This should not fail.
+            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+  }
+
+  @Test
+  public void testGeneratedKeepRulesWithMissingRuleAndDontObfuscateFollowedByD8() throws Exception {
+    parameters.assumeDexRuntime();
+
+    Path generatedKeepRules = temp.newFile("keep.rules").toPath();
+    TraceReferencesKeepRules keepRulesConsumer =
+        TraceReferencesKeepRules.builder()
+            // Don't obfuscate as D8 does not support apply mapping.
+            .setAllowObfuscation(false)
+            .setOutputPath(generatedKeepRules)
+            .build();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addSourceFiles(sourceJar)
+            .addTargetFiles(targetJar)
+            .setConsumer(keepRulesConsumer)
+            .build());
+
+    Path r8CompiledTarget =
+        testForR8(Backend.DEX)
+            .setMinApi(parameters)
+            .addProgramFiles(targetJar)
+            .addKeepRuleFiles(generatedKeepRules)
+            .addKeepRules("-keep class " + J.class.getTypeName() + " { m(); }")
+            // TODO(b/319190998): Adding dont obfuscate should not be needed as trace references is
+            //  already asked to not allow obfuscation. Hwing this will cause the CC class to not
+            //  get renamed.
+            .addDontObfuscate()
+            .compile()
+            .writeToZip();
+
+    testForD8(Backend.DEX)
+        .setMinApi(parameters)
+        .addClasspathFiles(targetJar)
+        .addProgramFiles(sourceJar)
+        .addRunClasspathFiles(r8CompiledTarget)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  // Interfaces I and J are in the target set for trace references.
+  interface I {
+    Object m();
+  }
+
+  interface J extends I {
+    default Object m() {
+      return "Hello, world!";
+    }
+  }
+
+  // Interfaces JImpl and Main are in the source set for trace references.
+  public static class JImpl implements J {}
+
+  public static class Main {
+
+    public static void m(I i) {
+      System.out.println(i.m());
+    }
+
+    public static void main(String[] args) {
+      m(new JImpl());
+    }
+  }
+}