Merge "Add failing test for default interface desugaring and apply mapping."
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 3bd33f7..0225784 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -131,6 +131,11 @@
   }
 
   @Override
+  public ExternalR8TestBuilder addApplyMapping(String proguardMap) {
+    throw new Unimplemented("No support for adding mapfile content yet");
+  }
+
+  @Override
   public ExternalR8TestBuilder addDataEntryResources(DataEntryResource... resources) {
     throw new Unimplemented("No support for adding data entry resources");
   }
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 06ddf44..f7af253 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -124,6 +124,11 @@
   }
 
   @Override
+  public JvmTestBuilder addLibraryClasses(Collection<Class<?>> classes) {
+    throw new Unimplemented("No support for changing the Java runtime library.");
+  }
+
+  @Override
   public JvmTestBuilder addProgramClasses(Collection<Class<?>> classes) {
     addProgramResources(ListUtils.map(classes, ClassFileResource::new));
     return self();
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 51dbd51..ac3b582 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -181,6 +181,11 @@
   }
 
   @Override
+  public ProguardTestBuilder addApplyMapping(String proguardMap) {
+    throw new Unimplemented("No support for adding mapfile content yet");
+  }
+
+  @Override
   public ProguardTestBuilder addDataEntryResources(DataEntryResource... resources) {
     throw new Unimplemented("No support for adding data entry resources");
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 4d0d9ae..01b6cee 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -12,11 +12,14 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -47,6 +50,7 @@
   private CollectingGraphConsumer graphConsumer = null;
   private List<String> keepRules = new ArrayList<>();
   private List<Path> mainDexRulesFiles = new ArrayList<>();
+  private List<String> applyMappingMaps = new ArrayList<>();
 
   @Override
   R8TestBuilder self() {
@@ -74,6 +78,21 @@
     builder.setDisableMinification(!enableMinification);
     builder.setProguardMapConsumer((string, ignore) -> proguardMapBuilder.append(string));
 
+    if (!applyMappingMaps.isEmpty()) {
+      try {
+        Path mappingsDir = getState().getNewTempFolder();
+        for (int i = 0; i < applyMappingMaps.size(); i++) {
+          String mapContent = applyMappingMaps.get(i);
+          Path mapPath = mappingsDir.resolve("mapping" + i + ".map");
+          FileUtils.writeTextFile(mapPath, mapContent);
+          builder.addProguardConfiguration(
+              Collections.singletonList("-applymapping " + mapPath.toString()), Origin.unknown());
+        }
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
     class Box {
       private List<ProguardConfigurationRule> syntheticProguardRules;
       private ProguardConfiguration proguardConfiguration;
@@ -240,6 +259,12 @@
     return self();
   }
 
+  @Override
+  public R8TestBuilder addApplyMapping(String proguardMap) {
+    applyMappingMaps.add(proguardMap);
+    return self();
+  }
+
   private void addInternalKeepRules(String... rules) {
     // We don't add these to the keep-rule set for other test provided rules.
     builder.addProguardConfiguration(Arrays.asList(rules), Origin.unknown());
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index fda7025..7e21839f 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -4,9 +4,14 @@
 
 package com.android.tools.r8;
 
+import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
 
 public abstract class TestBaseBuilder<
         C extends BaseCommand,
@@ -51,6 +56,35 @@
     return self();
   }
 
+  @Override
+  public T addLibraryClasses(Collection<Class<?>> classes) {
+    builder.addLibraryResourceProvider(
+        new ClassFileResourceProvider() {
+          final Map<String, ProgramResource> resources;
+
+          {
+            ImmutableMap.Builder<String, ProgramResource> builder = ImmutableMap.builder();
+            classes.forEach(
+                c ->
+                    builder.put(
+                        DescriptorUtils.javaTypeToDescriptor(c.getTypeName()),
+                        ProgramResource.fromFile(Kind.CF, ToolHelper.getClassFileForTestClass(c))));
+            resources = builder.build();
+          }
+
+          @Override
+          public Set<String> getClassDescriptors() {
+            return resources.keySet();
+          }
+
+          @Override
+          public ProgramResource getProgramResource(String descriptor) {
+            return resources.get(descriptor);
+          }
+        });
+    return self();
+  }
+
   public T addMainDexListFiles(Collection<Path> files) {
     builder.addMainDexListFiles(files);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index e6a5bbe..858e1bb 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -90,9 +90,7 @@
     return addLibraryClasses(Arrays.asList(classes));
   }
 
-  public T addLibraryClasses(Collection<Class<?>> classes) {
-    return addLibraryFiles(getFilesForClasses(classes));
-  }
+  public abstract T addLibraryClasses(Collection<Class<?>> classes);
 
   public T addLibraryFiles(Path... files) {
     return addLibraryFiles(Arrays.asList(files));
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index d74fab5..6883d1d 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.TestBase.Backend.DEX;
 
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -13,12 +14,15 @@
 import com.android.tools.r8.debug.DexDebugTestConfig;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
@@ -58,11 +62,43 @@
     }
   }
 
-  public CR addRunClasspath(List<Path> classpath) {
+  public CR addRunClasspathFiles(Path... classpath) {
+    return addRunClasspathFiles(Arrays.asList(classpath));
+  }
+
+  public CR addRunClasspathFiles(List<Path> classpath) {
     additionalRunClassPath.addAll(classpath);
     return self();
   }
 
+  public CR addRunClasspathClasses(Class<?>... classpath) {
+    return addRunClasspathClasses(Arrays.asList(classpath));
+  }
+
+  public CR addRunClasspathClasses(List<Class<?>> classpath) {
+    try {
+      Path path = state.getNewTempFolder().resolve("runtime-classes.jar");
+      ArchiveConsumer consumer = new ArchiveConsumer(path);
+      for (Class clazz : classpath) {
+        consumer.accept(
+            ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
+            DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
+            null);
+      }
+      consumer.finished(null);
+      additionalRunClassPath.addAll(Collections.singletonList(path));
+      return self();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public Path writeToZip() throws IOException {
+    Path file = state.getNewTempFolder().resolve("out.zip");
+    writeToZip(file);
+    return file;
+  }
+
   public CR writeToZip(Path file) throws IOException {
     app.writeToZip(file, getBackend() == DEX ? OutputMode.DexIndexed : OutputMode.ClassFile);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index a17ab09..7f428f9 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -107,6 +107,8 @@
     return self();
   }
 
+  public abstract T addApplyMapping(String proguardMap);
+
   private static String getMethodLine(MethodReference method) {
     // Should we encode modifiers in method references?
     StringBuilder builder = new StringBuilder();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..6c3af2b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2019, 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.naming.applymapping.desugar;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+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 DefaultInterfaceMethodTest extends TestBase {
+
+  public static final String OUTPUT = "Called LibraryInterface::foo";
+  public static final String EXPECTED = StringUtils.lines(OUTPUT);
+
+  public interface LibraryInterface {
+    default void foo() {
+      System.out.println(OUTPUT);
+    }
+  }
+
+  public static class ProgramClass implements LibraryInterface {
+
+    public static void main(String[] args) {
+      new ProgramClass().foo();
+    }
+  }
+
+  @Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  private final Backend backend;
+
+  public DefaultInterfaceMethodTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void testJvm() throws Throwable {
+    Assume.assumeTrue(backend == Backend.CF);
+    testForJvm()
+        .addProgramClasses(LibraryInterface.class, ProgramClass.class)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testFullProgram() throws Throwable {
+    testForR8(backend)
+        .addProgramClasses(LibraryInterface.class, ProgramClass.class)
+        .addKeepMainRule(ProgramClass.class)
+        .run(ProgramClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testLibraryLinkedWithProgram() throws Throwable {
+    R8TestCompileResult libraryResult =
+        testForR8(backend)
+            .addProgramClasses(LibraryInterface.class)
+            .addKeepRules("-keep class " + LibraryInterface.class.getTypeName() + " { *; }")
+            .compile();
+
+    R8TestRunResult result =
+        testForR8(backend)
+            .noTreeShaking()
+            .noMinification()
+            .addProgramClasses(ProgramClass.class)
+            .addLibraryClasses(LibraryInterface.class)
+            .addApplyMapping(libraryResult.getProguardMap())
+            .compile()
+            .addRunClasspathFiles(libraryResult.writeToZip())
+            .run(ProgramClass.class);
+
+    if (backend == Backend.DEX && willDesugarDefaultInterfaceMethods()) {
+      // TODO(b/127779880): The use of the default lambda will fail in case of desugaring.
+      result.assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
+    } else {
+      result.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  private static boolean willDesugarDefaultInterfaceMethods() {
+    return ToolHelper.getMinApiLevelForDexVm().getLevel() < AndroidApiLevel.N.getLevel();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index dd11dc1..ffe6c21 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -177,7 +177,7 @@
               syntheticProguardRules ->
                   checkSynthesizedRuleExpectation(syntheticProguardRules, synthesizedRule))
           .inspect(inspector)
-          .addRunClasspath(ImmutableList.of(buildMockAndroidRuntimeLibrary(runtimeApiLevel)))
+          .addRunClasspathFiles(ImmutableList.of(buildMockAndroidRuntimeLibrary(runtimeApiLevel)))
           .run(mainClassName)
           .assertSuccessWithOutput(expectedOutput);
     } else {
@@ -188,7 +188,7 @@
           .addKeepRules(additionalKeepRules)
           .compile()
           .inspectProguardConfiguration(this::noSynthesizedRules)
-          .addRunClasspath(
+          .addRunClasspathFiles(
               ImmutableList.of(mockAndroidRuntimeLibrary(AndroidApiLevel.D.getLevel())))
           .run(mainClassName)
           .assertSuccessWithOutput(expectedResultForCompat(AndroidApiLevel.D));