Add a reproduction of b/182381011

Bug: 182381011
Change-Id: I5ba6d4812db0e402466cb67df2ae43658d3bc3f8
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomMapHierarchyTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomMapHierarchyTest.java
new file mode 100644
index 0000000..9984eb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomMapHierarchyTest.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Assume;
+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 CustomMapHierarchyTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+        BooleanUtils.values());
+  }
+
+  public CustomMapHierarchyTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.parameters = parameters;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  }
+
+  @Test
+  public void testWithoutLibraryDesugaring() throws Exception {
+    Assume.assumeTrue(
+        parameters.getRuntime().isCf()
+            || parameters
+                .getApiLevel()
+                .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport()));
+    testForRuntime(parameters)
+        .addInnerClasses(CustomMapHierarchyTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    Assume.assumeTrue(parameters.getRuntime().isDex());
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForD8()
+        .addInnerClasses(CustomMapHierarchyTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/182381011): Should always be EXPECTED.
+        .assertSuccessWithOutput(
+            parameters
+                    .getApiLevel()
+                    .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+                ? EXPECTED
+                : NOT_EXPECTED);
+  }
+
+  @Test
+  public void testD8Cf2Cf() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+
+    Path jar =
+        testForD8(Backend.CF)
+            .addInnerClasses(CustomMapHierarchyTest.class)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .setIncludeClassesChecksum(true)
+            .setMinApi(parameters.getApiLevel())
+            .allowStdoutMessages()
+            .compile()
+            .writeToZip();
+    String desugaredLibraryKeepRules = "";
+    if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+      // Collection keep rules is only implemented in the DEX writer.
+      assertEquals(0, keepRuleConsumer.get().length());
+      desugaredLibraryKeepRules = "-keep class * { *; }";
+    }
+    if (parameters.getRuntime().isDex()) {
+      testForD8()
+          .addProgramFiles(jar)
+          .setMinApi(parameters.getApiLevel())
+          .disableDesugaring()
+          .allowStdoutMessages()
+          .compile()
+          .addDesugaredCoreLibraryRunClassPath(
+              this::buildDesugaredLibrary,
+              parameters.getApiLevel(),
+              desugaredLibraryKeepRules,
+              shrinkDesugaredLibrary)
+          .run(parameters.getRuntime(), Main.class)
+          // TODO(b/182381011): Should always be EXPECTED.
+          .assertSuccessWithOutput(
+              parameters
+                      .getApiLevel()
+                      .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+                  ? EXPECTED
+                  : NOT_EXPECTED);
+    } else {
+      testForJvm()
+          .addProgramFiles(jar)
+          .addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {}))
+          .run(parameters.getRuntime(), Main.class)
+          // TODO(b/182381011): Should always be EXPECTED.
+          .assertSuccessWithOutput(
+              parameters
+                      .getApiLevel()
+                      .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+                  ? EXPECTED
+                  : NOT_EXPECTED);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Assume.assumeTrue(parameters.getRuntime().isDex());
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .addInnerClasses(CustomMapHierarchyTest.class)
+        .addKeepMainRule(Main.class)
+        .addKeepAllClassesRuleWithAllowObfuscation()
+        .addKeepAttributes(
+            ProguardKeepAttributes.SIGNATURE,
+            ProguardKeepAttributes.INNER_CLASSES,
+            ProguardKeepAttributes.ENCLOSING_METHOD)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        // TODO(b/182381011): Should not happen.
+        .applyIf(
+            parameters
+                .getApiLevel()
+                .isLessThan(TestBase.apiLevelWithDefaultInterfaceMethodsSupport()),
+            b -> b.addDontWarn("java.util.Map$-CC"))
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/182381011): Should always be EXPECTED.
+        .assertSuccessWithOutput(
+            parameters
+                    .getApiLevel()
+                    .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+                ? EXPECTED
+                : NOT_EXPECTED);
+  }
+
+  final String EXPECTED =
+      StringUtils.lines("B::getOrDefault", "default 1", "B::getOrDefault", "default 2");
+  final String NOT_EXPECTED = StringUtils.lines("B::getOrDefault", "default 1", "default 2");
+
+  public static class Main {
+    public static void main(String[] args) {
+      System.out.println(new B<String>().getOrDefault("Not found", "default 1"));
+      System.out.println(
+          ((Map<String, String>) new B<String>()).getOrDefault("Not found", "default 2"));
+    }
+  }
+
+  abstract static class A<T> extends LinkedHashMap<T, T> {}
+
+  // AbstractSequentialList implements List further up the hierarchy.
+  static class B<T> extends A<T> {
+    // Need at least one overridden default method for emulated dispatch.
+    @Override
+    public T getOrDefault(Object key, T defaultValue) {
+      System.out.println("B::getOrDefault");
+      return super.getOrDefault(key, defaultValue);
+    }
+  }
+}