Loosen desugaring constraints

And enable all desugaring by by default.

Loosening as defined in the discussion document:
- Skip desugaring of anything if the target method is defined in the
  bootclasspath (i.e. API android.jar).
- Just warn about invokestatic, invokesuper and call to method handle
  when the target class or interface is missing.
- Change handling of hierarchy:
  - If all declared interfaces (and their super interfaces hierarchies)
    of a class are known and the class has an implementation for all
    default methods found in the declared interface of the class (even
    those hidden in subinterfaces), skip default method desugaring for
    the class.
  - In any other case of missing element in the hierachy is reported as
    a warning. missing interfaces are ignored, missing class cause the
    subclasses to be ignored during default method desugaring.

Change-Id: I8d7043616bbfff26ac09a17a3e953a4a4e5aa4c8
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5b2e5cf..76779bb 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -217,8 +217,6 @@
     internal.skipMinification = true;
     assert internal.useTreeShaking;
     internal.useTreeShaking = false;
-    assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
-    assert internal.tryWithResourcesDesugaring == OffOrAuto.Off;
     assert internal.inlineAccessors;
     internal.inlineAccessors = false;
     assert internal.removeSwitchMaps;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index b7036f5..d3964d1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -42,9 +44,7 @@
   }
 
   final void process(DexClass clazz) {
-    if (clazz.isInterface()) {
-      throw new CompilationError("Interface in superclass chain.");
-    }
+    assert !clazz.isInterface();
     if (!clazz.isProgramClass()) {
       // We assume that library classes don't need to be processed, since they
       // are provided by a runtime not supporting default interface methods.
@@ -61,8 +61,15 @@
     // about methods added to superclasses when we decide if we want to add a default
     // method to class `clazz`.
     DexType superType = clazz.superType;
-    if (superType != null && superType != rewriter.factory.objectType) {
-      process(rewriter.findRequiredClass(superType));
+    // If superClass definition is missing, just skip this part and let real processing of its
+    // subclasses report the error if it is required.
+    DexClass superClass = superType == null ? null : rewriter.findDefinitionFor(superType);
+    if (superClass != null && superType != rewriter.factory.objectType) {
+      if (superClass.isInterface()) {
+        throw new CompilationError("Interface `" + superClass.toSourceString()
+        + "` used as super class of `" + clazz.toSourceString() + "`.");
+      }
+      process(superClass);
     }
 
     if (clazz.interfaces.isEmpty()) {
@@ -95,7 +102,10 @@
 
   private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
     DexMethod method = defaultMethod.method;
-    assert !rewriter.findRequiredClass(method.holder).isLibraryClass();
+    // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
+    // even if this results in invalid code, these classes are never desugared.
+    assert rewriter.findDefinitionFor(method.holder) != null
+        && !rewriter.findDefinitionFor(method.holder).isLibraryClass();
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
     DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
@@ -113,23 +123,62 @@
   // in this class.
   private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) {
     DefaultMethodsHelper helper = new DefaultMethodsHelper();
-
+    DexClass current = clazz;
+    List<DexEncodedMethod> accumulatedVirtualMethods = new ArrayList<>();
     // Collect candidate default methods by inspecting interfaces implemented
     // by this class as well as its superclasses.
-    DexClass current = clazz;
-    while (true) {
+    //
+    // We assume here that interfaces implemented by java.lang.Object don't
+    // have default methods to desugar since they are library interfaces. And we assume object
+    // methods don't hide any default interface methods. Default interface method matching Object's
+    // methods is supposed to fail with a compilation error.
+    // Note that this last assumption will be broken if Object API is augmented with a new method in
+    // the future.
+    while (current.type != rewriter.factory.objectType) {
       for (DexType type : current.interfaces.values) {
-        helper.merge(getOrCreateInterfaceInfo(type));
+        helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
       }
 
-      DexType superType = current.superType;
-      if (superType == null || superType == rewriter.factory.objectType) {
-        // We assume here that interfaces implemented by java.lang.Object don't
-        // have default methods and don't hide any default interface methods since
-        // they must be library interfaces.
-        break;
+      accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
+
+      List<DexEncodedMethod> defaultMethodsInDirectInterface = helper.createFullList();
+      List<DexEncodedMethod> toBeImplementedFromDirectInterface =
+          new ArrayList<>(defaultMethodsInDirectInterface.size());
+      hideCandidates(accumulatedVirtualMethods,
+          defaultMethodsInDirectInterface,
+          toBeImplementedFromDirectInterface);
+      // toBeImplementedFromDirectInterface are those that we know for sure we need to implement by
+      // looking at the already desugared super classes.
+      // Remaining methods in defaultMethodsInDirectInterface are those methods we need to look at
+      // the hierarchy to know how they should be handled.
+      if (toBeImplementedFromDirectInterface.isEmpty()
+          && defaultMethodsInDirectInterface.isEmpty()) {
+        // No interface with default in direct hierarchy, nothing to do: super already has all that
+        // is needed.
+        return Collections.emptyList();
       }
-      current = rewriter.findRequiredClass(superType);
+
+      if (current.superType == null) {
+        break;
+      } else {
+        DexClass superClass = rewriter.findDefinitionFor(current.superType);
+        if (superClass != null) {
+          current = superClass;
+        } else {
+          String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
+          if (current == clazz) {
+            message += " because its super class `" + clazz.superType.toSourceString()
+            + "` is missing";
+          } else {
+            message +=
+                " because it's hierarchy is incomplete. The class `"
+                    + current.superType.toSourceString()
+                    + "` is missing and it is the declared super class of `"
+                    + current.toSourceString() + "`";
+          }
+          throw new CompilationError(message);
+        }
+      }
     }
 
     List<DexEncodedMethod> candidates = helper.createCandidatesList();
@@ -142,25 +191,41 @@
     current = clazz;
     while (true) {
       // Hide candidates by virtual method of the class.
-      hideCandidates(current.virtualMethods(), candidates, toBeImplemented);
+      hideCandidates(Arrays.asList(current.virtualMethods()), candidates, toBeImplemented);
       if (candidates.isEmpty()) {
         return toBeImplemented;
       }
 
       DexType superType = current.superType;
-      if (superType == null || superType == rewriter.factory.objectType) {
+      DexClass superClass = null;
+      if (superType != null) {
+        superClass = rewriter.findDefinitionFor(superType);
+        // It's available or we would have failed while analyzing the hierarchy for interfaces.
+        assert superClass != null;
+      }
+      if (superClass == null || superType == rewriter.factory.objectType) {
         // Note that default interface methods must never have same
         // name/signature as any method in java.lang.Object (JLS ยง9.4.1.2).
 
         // Everything still in candidate list is not hidden.
         toBeImplemented.addAll(candidates);
+        // NOTE: we also intentionally remove all candidates coming from android.jar
+        // since it is only possible in case v24+ version of android.jar is provided.
+        // WARNING: This may result in incorrect code on older platforms!
+        toBeImplemented.removeIf(
+            method -> {
+              DexClass holder = rewriter.findDefinitionFor(method.method.holder);
+              // Holder of a found method to implement is a defined interface.
+              assert holder != null;
+              return holder.isLibraryClass();
+            });
         return toBeImplemented;
       }
-      current = rewriter.findRequiredClass(superType);
+      current = superClass;
     }
   }
 
-  private void hideCandidates(DexEncodedMethod[] virtualMethods,
+  private void hideCandidates(List<DexEncodedMethod> virtualMethods,
       List<DexEncodedMethod> candidates, List<DexEncodedMethod> toBeImplemented) {
     Iterator<DexEncodedMethod> it = candidates.iterator();
     while (it.hasNext()) {
@@ -188,42 +253,56 @@
     }
   }
 
-  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) {
+  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
     DefaultMethodsHelper.Collection collection = cache.get(iface);
     if (collection != null) {
       return collection;
     }
-    collection = createInterfaceInfo(iface);
+    collection = createInterfaceInfo(classToDesugar, implementing, iface);
     cache.put(iface, collection);
     return collection;
   }
 
-  private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) {
+  private DefaultMethodsHelper.Collection createInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
     DefaultMethodsHelper helper = new DefaultMethodsHelper();
-    DexClass clazz = rewriter.findRequiredClass(iface);
-    if (!clazz.isInterface()) {
-      throw new CompilationError(
-          "Type " + iface.toSourceString() + " is expected to be an interface.");
+    DexClass definedInterface = rewriter.findDefinitionFor(iface);
+    if (definedInterface == null) {
+      String message = "Interface `" + iface.toSourceString()
+              + "` not found. It's needed to make sure desugaring of `"
+              + classToDesugar.toSourceString() + "` is correct. Desugaring will assume that this"
+              + " interface has no default method";
+      if (classToDesugar != implementing) {
+        message += ". This missing interface is declared in the direct hierarchy of `"
+              + implementing.toString() + "`";
+      }
+      rewriter.warnMissingClass(iface, message);
+      return helper.wrapInCollection();
     }
-    if (clazz.isLibraryClass()) {
-      // For library interfaces we always assume there are no default
-      // methods, since the interface is part of framework provided by
-      // runtime which does not support default interface methods.
-      return DefaultMethodsHelper.Collection.EMPTY;
+
+    if (!definedInterface.isInterface()) {
+      throw new CompilationError(
+          "Type " + iface.toSourceString() + " is referenced as an interface of `"
+          + implementing.toString() + "`.");
     }
 
     // Merge information from all superinterfaces.
-    for (DexType superinterface : clazz.interfaces.values) {
-      helper.merge(getOrCreateInterfaceInfo(superinterface));
+    for (DexType superinterface : definedInterface.interfaces.values) {
+      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
     }
 
     // Hide by virtual methods of this interface.
-    for (DexEncodedMethod virtual : clazz.virtualMethods()) {
+    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
       helper.hideMatches(virtual.method);
     }
 
     // Add all default methods of this interface.
-    for (DexEncodedMethod encoded : clazz.virtualMethods()) {
+    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
       if (rewriter.isDefaultMethod(encoded)) {
         helper.addDefaultMethod(encoded);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
index 2f25b19..ae9a2ae 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -102,6 +104,18 @@
     return candidates;
   }
 
+  final List<DexEncodedMethod> createFullList() {
+    if (candidates.isEmpty() && hidden.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<DexEncodedMethod> fullList =
+        new ArrayList<DexEncodedMethod>(candidates.size() + hidden.size());
+    fullList.addAll(candidates);
+    fullList.addAll(hidden);
+    return fullList;
+  }
+
   // Create default interface collection based on collected information.
   final Collection wrapInCollection() {
     candidates.removeAll(hidden);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index cbeda1e..aa93aa8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
@@ -25,6 +26,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.logging.Log;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
@@ -68,6 +70,11 @@
   // to this collection since it is only filled in ClassProcessor running synchronously.
   private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
 
+  /**
+   * A set of dexitems we have reported missing to dedupe warnings.
+   */
+  private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
+
   /** Defines a minor variation in desugaring. */
   public enum Flavor {
     /** Process all application resources. */
@@ -100,10 +107,11 @@
           // Check that static interface methods are not referenced
           // from invoke-custom instructions via method handles.
           DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
-          reportStaticInterfaceMethodHandle(callSite.bootstrapMethod);
+          reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod);
           for (DexValue arg : callSite.bootstrapArgs) {
             if (arg instanceof DexValue.DexValueMethodHandle) {
-              reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value);
+              reportStaticInterfaceMethodHandle(encodedMethod.method,
+                  ((DexValue.DexValueMethodHandle) arg).value);
             }
           }
           continue;
@@ -112,7 +120,17 @@
         if (instruction.isInvokeStatic()) {
           InvokeStatic invokeStatic = instruction.asInvokeStatic();
           DexMethod method = invokeStatic.getInvokedMethod();
-          if (isInterfaceClass(method.holder)) {
+          DexClass clazz = findDefinitionFor(method.holder);
+          if (clazz == null) {
+            // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+            // exception but we can not report it as error since it can also be the intended
+            // behavior.
+            warnMissingClass(encodedMethod.method, method.holder);
+          } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
+            // NOTE: we intentionally don't desugar static calls into static interface
+            // methods coming from android.jar since it is only possible in case v24+
+            // version of android.jar is provided.
+            // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(staticAsMethodOfCompanionClass(method),
@@ -124,7 +142,17 @@
         if (instruction.isInvokeSuper()) {
           InvokeSuper invokeSuper = instruction.asInvokeSuper();
           DexMethod method = invokeSuper.getInvokedMethod();
-          if (isInterfaceClass(method.holder)) {
+          DexClass clazz = findDefinitionFor(method.holder);
+          if (clazz == null) {
+            // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+            // exception but we can not report it as error since it can also be the intended
+            // behavior.
+            warnMissingClass(encodedMethod.method, method.holder);
+          } else if (clazz != null && (clazz.isInterface() && !clazz.isLibraryClass())) {
+            // NOTE: we intentionally don't desugar super calls into interface methods
+            // coming from android.jar since it is only possible in case v24+ version
+            // of android.jar is provided.
+            // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(defaultAsMethodOfCompanionClass(method),
@@ -135,25 +163,27 @@
     }
   }
 
-  private void reportStaticInterfaceMethodHandle(DexMethodHandle handle) {
-    if (handle.type.isInvokeStatic() && isInterfaceClass(handle.asMethod().holder)) {
-      throw new Unimplemented(
-          "Desugaring of static interface method handle in is not yet supported.");
+  private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
+    if (handle.type.isInvokeStatic()) {
+      DexClass holderClass = findDefinitionFor(handle.asMethod().holder);
+      // NOTE: If the class definition is missing we can't check. Let it be handled as any other
+      // missing call target.
+      if (holderClass == null) {
+        warnMissingClass(referencedFrom, handle.asMethod().holder);
+      } else if (holderClass.isInterface()) {
+        throw new Unimplemented(
+            "Desugaring of static interface method handle as in `"
+            + referencedFrom.toSourceString() + "` in is not yet supported.");
+      }
     }
   }
 
-  private boolean isInterfaceClass(DexType type) {
-    return findRequiredClass(type).isInterface();
-  }
-
-  // Returns the class for the type specified, report errors for missing classes.
-  final DexClass findRequiredClass(DexType type) {
-    DexClass clazz = converter.appInfo.definitionFor(type);
-    if (clazz != null) {
-      return clazz;
-    }
-    throw new CompilationError("Type '" + type.toSourceString() +
-        "' required for default and static interface methods desugaring not found.");
+  /**
+   * Returns the class definition for the specified type.
+   * @return may return null if no definition for the given type is available.
+   */
+  final DexClass findDefinitionFor(DexType type) {
+    return converter.appInfo.definitionFor(type);
   }
 
   // Gets the companion class for the interface `type`.
@@ -262,4 +292,18 @@
     }
     return true;
   }
+
+  void warnMissingClass(DexType missing, String message) {
+    // TODO replace by a proper warning mechanic (see b/65154707).
+    // TODO think about using a common deduplicating mechanic with Enqueuer
+    if (reportedMissing.add(missing)) {
+      System.err.println(message);
+    }
+  }
+
+  private void warnMissingClass(DexItem referencedFrom, DexType clazz) {
+      warnMissingClass(clazz,
+          "Type `" + clazz.toSourceString() + "` was not found, it is required for default or"
+          + " static interface methods desugaring of `" + referencedFrom.toSourceString() + "`");
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c241bd7..f27998c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -84,9 +84,9 @@
   public List<String> logArgumentsFilter = ImmutableList.of();
 
   // Defines interface method rewriter behavior.
-  public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
+  public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
   // Defines try-with-resources rewriter behavior.
-  public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Off;
+  public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto;
 
   // Application writing mode.
   public OutputMode outputMode = OutputMode.Indexed;
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
new file mode 100644
index 0000000..bec4e67
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2017, 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 desugaringwithandroidjar25;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+public class DefaultMethodInAndroidJar25 {
+  public static void main(String[] args) throws Exception {
+    ClassWithDefaultPlatformMethods.test();
+  }
+}
+
+class ClassWithDefaultPlatformMethods implements Collection<String> {
+  private final ArrayList<String> list =
+      new ArrayList<String>() {{
+        add("First");
+        add("Second");
+      }};
+
+  static void test() {
+    ClassWithDefaultPlatformMethods instance = new ClassWithDefaultPlatformMethods();
+    instance.forEach(x -> System.out.println("BEFORE: " + x));
+    instance.removeIf(x -> true);
+    instance.forEach(x -> System.out.println("AFTER: " + x));
+  }
+
+  @Override
+  public int size() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean contains(Object o) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public Iterator<String> iterator() {
+    return list.iterator();
+  }
+
+  @Override
+  public Object[] toArray() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public <T> T[] toArray(T[] a) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean add(String s) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean remove(Object o) {
+    return list.remove(o);
+  }
+
+  @Override
+  public boolean containsAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean addAll(Collection<? extends String> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean removeAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean retainAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public void clear() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean removeIf(Predicate<? super String> filter) {
+    return Collection.super.removeIf(filter);
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
new file mode 100644
index 0000000..f07ea8f
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, 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 desugaringwithandroidjar25;
+
+import java.util.Comparator;
+
+public class StaticMethodInAndroidJar25 {
+  public static void main(String[] args) throws Exception {
+    Comparator<String> comparing =
+        Comparator.comparing(x -> x, String::compareTo);
+    System.out.println("'a' <> 'b' = " + comparing.compare("a", "b"));
+    System.out.println("'b' <> 'b' = " + comparing.compare("b", "b"));
+    System.out.println("'c' <> 'b' = " + comparing.compare("c", "b"));
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
new file mode 100644
index 0000000..98504bd
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib1;
+
+public interface A {
+  default String foo() {
+    return "A";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
new file mode 100644
index 0000000..ff42039
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib1;
+
+public interface A2 {
+  default String foo() {
+    return "A2";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
new file mode 100644
index 0000000..1ab424e
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib2;
+
+import desugaringwithmissingclasslib1.A;
+
+public interface B extends A {
+  @Override
+  default String foo() {
+    return "B";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
new file mode 100644
index 0000000..029f9f4
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib3;
+
+import desugaringwithmissingclasslib1.A;
+
+public class C implements A {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
new file mode 100644
index 0000000..b2aee3b
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasslib4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+  public String foo2() {
+    return "C2";
+  }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..5f3c1c2
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest1;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib2.B;
+
+public class ImplementMethodsWithDefault implements A, B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
new file mode 100644
index 0000000..411a016
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest1;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..f2ef1b6
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest2;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
new file mode 100644
index 0000000..c42ac81
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest2;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..4826237
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest3;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+
+  public String getB() {
+    return B.super.foo();
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
new file mode 100644
index 0000000..bde4d09
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest3;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String b = instance.getB();
+      if (b.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + b);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
new file mode 100644
index 0000000..cea6324
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+  public String foo2() {
+    return "C2";
+  }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..c62bc2c
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
new file mode 100644
index 0000000..e439c5c
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest4;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo2();
+      if (foo.equals("C2")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..f8dfc3b
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest5;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+import desugaringwithmissingclasslib4.C2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
new file mode 100644
index 0000000..d1ee997
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, 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 desugaringwithmissingclasstest5;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo2();
+      if (foo.equals("C2")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 803d760..2159d6b 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -38,14 +38,14 @@
 public abstract class D8IncrementalRunExamplesAndroidOTest
     extends RunExamplesAndroidOTest<D8Command.Builder> {
 
-  abstract class D8IncrementalTestRunner extends TestRunner {
+  abstract class D8IncrementalTestRunner extends TestRunner<D8IncrementalTestRunner> {
 
     D8IncrementalTestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    D8IncrementalTestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
@@ -154,12 +154,11 @@
         builder = transformation.apply(builder);
       }
       builder = builder.setOutputMode(outputMode);
-      builder = builder.addLibraryFiles(
-          Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
       if (output != null) {
         builder = builder.setOutputPath(output);
       }
-      addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
       D8Command command = builder.build();
       try {
         return ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -288,6 +287,7 @@
     Assert.assertArrayEquals(expectedFileNames, dexFiles);
   }
 
+  @Override
   abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass);
 
   static byte[] readFromResource(Resource resource) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index d707614..c2ac969 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -52,6 +52,11 @@
       builder.addLibraryResourceProvider(
           PreloadedClassFileProvider.fromArchive(location));
     }
+
+    @Override
+    D8LazyTestRunner self() {
+      return this;
+    }
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index 5b2977c..99fe81c 100644
--- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -23,7 +23,13 @@
 
     @Override
     void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
-      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
+    }
+
+    @Override
+    D8LazyTestRunner self() {
+      return this;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 6f51624..7a56e3c 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -4,33 +4,58 @@
 
 package com.android.tools.r8;
 
+import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.OffOrAuto;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.function.UnaryOperator;
+import org.hamcrest.core.CombinableMatcher;
+import org.hamcrest.core.IsInstanceOf;
+import org.hamcrest.core.StringContains;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.internal.matchers.ThrowableMessageMatcher;
 
 public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
 
-  class D8TestRunner extends TestRunner {
+  class D8TestRunner extends TestRunner<D8TestRunner> {
 
     D8TestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    D8TestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
+    D8TestRunner withClasspath(Path... classpath) {
+      return withBuilderTransformation(b -> {
+        try {
+          return b.addClasspathFiles(classpath);
+        } catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      });
+    }
+
+
     @Override
     void build(Path inputFile, Path out) throws Throwable {
       D8Command.Builder builder = D8Command.builder();
       for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
         builder = transformation.apply(builder);
       }
-      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      builder.addLibraryFiles(
+          Paths.get(
+              ToolHelper.getAndroidJar(
+                  androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
       D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
       try {
         ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -40,10 +65,462 @@
         throw re.getCause() == null ? re : re.getCause();
       }
     }
+
+    @Override
+    D8TestRunner self() {
+      return this;
+    }
   }
 
+  @Test
+  public void testDefaultInInterfaceWithoutDesugaring() throws Throwable {
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Off)
+            .withMinApiLevel(ANDROID_K_API);
+    try  {
+      lib1.build();
+
+      // compilation should have failed on CompilationError since A is declaring a default method.
+      Assert.fail();
+    } catch (CompilationError | CompilationException e) {
+      // Expected.
+    }
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared() throws Throwable {
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(ANDROID_K_API);
+    lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(ANDROID_K_API);
+    lib2.build();
+
+    // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation
+    // from B.
+    // test is compiled with incomplete classpath: lib2 is missing so ImplementMethodsWithDefault is
+    // missing one of it interfaces.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(ANDROID_K_API);
+    test.build();
+
+    // TODO check compilation warnings are correctly reported
+    // B is missing so compiled code makes no sense, no need to test execution.
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared2AndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+    // ImplementMethodsWithDefault is missing all its hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    // TODO check compilation warnings are correctly reported
+
+    // Missing interface B is causing the wrong code to be executed.
+    if (ToolHelper.artSupported()) {
+      thrown.expect(AssertionError.class);
+      execute(
+          "testMissingInterfaceDesugared2AndroidK",
+          "desugaringwithmissingclasstest2.Main",
+          new Path[] {
+              lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+          },
+          new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+    }
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared2AndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+    // ImplementMethodsWithDefault is missing all its hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    execute(
+        "testMissingInterfaceDesugared2AndroidO",
+        "desugaringwithmissingclasstest2.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable {
+
+    int minApi = ANDROID_K_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B
+    // { String getB() { return B.super.foo(); }
+    // Should be able to call implementation from B.
+    // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    // TODO check compilation warnings are correctly reported
+
+    // Missing interface B is causing the wrong method to be executed.
+    if (ToolHelper.artSupported()) {
+      thrown.expect(AssertionError.class);
+      execute(
+          "testCallToMissingSuperInterfaceDesugaredAndroidK",
+          "desugaringwithmissingclasstest3.Main",
+          new Path[] {
+              lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+          },
+          new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+    }
+  }
+
+  @Test
+  public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B
+    // { String getB() { return B.super.foo(); }
+    // Should be able to call implementation from B.
+    // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    execute(
+        "testCallToMissingSuperInterfaceDesugaredAndroidO",
+        "desugaringwithmissingclasstest3.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testMissingSuperDesugaredAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // ImplementMethodsWithDefault is missing its super class.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withClasspath(lib2.getInputJar())
+            .withMinApiLevel(minApi);
+    thrown.expect(
+        new CombinableMatcher<CompilationError>(new IsInstanceOf(CompilationError.class))
+        .and(new ThrowableMessageMatcher<CompilationError>(
+            new StringContains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault")))
+        .and(new ThrowableMessageMatcher<CompilationError>(
+            new StringContains("desugaringwithmissingclasslib3.C"))));
+    test.build();
+  }
+
+  @Test
+  public void testMissingSuperDesugaredAndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // ImplementMethodsWithDefault is missing its super class.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withClasspath(lib2.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidO",
+        "desugaringwithmissingclasstest2.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    //       interface A2 { default String foo2() { return "A2"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib3: class C { /* content irrelevant }
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class C2 extends C { public String foo2() { return "C2"; } }
+    //       class ImplementMethodsWithDefault extends C2 implements A, A2 {
+    //            public String foo() { return "ImplementMethodsWithDefault"; }
+    //       }
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // C2 is missing its super class. But desugaring should be OK since all
+    // interface methods are explicitly defined in program classes of the hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+        "desugaringwithmissingclasstest4.Main",
+        new Path[] {
+          lib1.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib3Dex, testDex});
+
+  }
+
+  @Test
+  public void testMissingSuperDesugaredWithClasspathCrossImplementationAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    //       interface A2 { default String foo2() { return "A2"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib3: class C { /* content irrelevant }
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // lib4: class C2 extends C { public String foo2() { return "C2"; } }
+    // lib4 is compiled with full classpath
+    D8TestRunner lib4 =
+        test("desugaringwithmissingclasslib4", "desugaringwithmissingclasslib4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib4Dex = lib4.build();
+
+    // test: class ImplementMethodsWithDefault extends C2 implements A, A2 {
+    //            public String foo() { return "ImplementMethodsWithDefault"; }
+    //       }
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // C2 is missing its super class. But desugaring should be OK since all
+    // interface methods are explicitly defined in program classes of the hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib4.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+        "desugaringwithmissingclasstest4.Main",
+        new Path[] {
+          lib1.getInputJar(), lib3.getInputJar(), lib4.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
+
+  }
   @Override
-  TestRunner test(String testName, String packageName, String mainClass) {
+  D8TestRunner test(String testName, String packageName, String mainClass) {
     return new D8TestRunner(testName, packageName, mainClass);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 8ffab3c..7cb47e2 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -142,21 +142,6 @@
           .put("975-iface-private", Constants.ANDROID_N_API)
           .build();
 
-  // Tests requiring interface method desugaring because they explicitly or
-  // implicitly define static or default interface methods and are enabled
-  // on pre-N Android.
-  private static List<String> enableInterfaceMethodDesugaring =
-      ImmutableList.of(
-          "563-checker-invoke-super",
-          "604-hot-static-interface",
-          "961-default-iface-resolution-gen",
-          "963-default-range-smali",
-          "965-default-verify",
-          "967-default-ame",
-          "969-iface-super",
-          "978-virtual-interface"
-      );
-
   // Tests that timeout when run with Art.
   private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt =
       new ImmutableListMultimap.Builder<String, TestCondition>()
@@ -205,8 +190,9 @@
 
   // Tests that are never compiled or run.
   private static List<String> skipAltogether = ImmutableList.of(
-      // This test contains an invalid type hierarchy, which we cannot currently handle.
+      // Those tests contains an invalid type hierarchy, which we cannot currently handle.
       "065-mismatched-implements",
+      "066-mismatched-super",
       // This test contains invalid dex code that reads uninitialized registers after an
       // an instruction that would in any case throw (implicit via aget null 0).
       "706-jit-skip-compilation",
@@ -1130,7 +1116,12 @@
         Integer minSdkVersion = needMinSdkVersion.get(name);
         if (minSdkVersion != null) {
           builder.setMinApiLevel(minSdkVersion);
+          builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdkVersion)));
+        } else {
+          builder.addLibraryFiles(Paths.get(
+              ToolHelper.getAndroidJar(Constants.DEFAULT_ANDROID_API)));
         }
+
         D8Output output = D8.run(builder.build());
         output.write(Paths.get(resultPath));
         break;
@@ -1153,9 +1144,6 @@
         ToolHelper.runR8(
             builder.build(),
             options -> {
-              if (enableInterfaceMethodDesugaring.contains(name)) {
-                options.interfaceMethodDesugaring = OffOrAuto.Auto;
-              }
               if (disableInlining) {
                 options.inlineAccessors = false;
               }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 6c45f40..e63b155 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -46,14 +46,14 @@
         .run();
   }
 
-  class R8TestRunner extends TestRunner {
+  class R8TestRunner extends TestRunner<R8TestRunner> {
 
     R8TestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    R8TestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
@@ -64,16 +64,23 @@
         for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
           builder = transformation.apply(builder);
         }
+        builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+            androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
         R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
         ToolHelper.runR8(command, this::combinedOptionConsumer);
       } catch (ExecutionException e) {
         throw e.getCause();
       }
     }
+
+    @Override
+    R8TestRunner self() {
+      return this;
+    }
   }
 
   @Override
-  TestRunner test(String testName, String packageName, String mainClass) {
+  R8TestRunner test(String testName, String packageName, String mainClass) {
     return new R8TestRunner(testName, packageName, mainClass);
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
index 7594b0c..9d745f6 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
@@ -133,7 +133,8 @@
     thrown.expect(ApiLevelException.class);
     test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
         "StaticInterfaceMethods")
-        .run();
+        .withInterfaceMethodDesugaring(OffOrAuto.Off)
+       .run();
   }
 
   @Test
@@ -149,6 +150,7 @@
     thrown.expect(ApiLevelException.class);
     test("defaultmethods-error-due-to-min-sdk", "interfacemethods",
         "DefaultMethods")
+        .withInterfaceMethodDesugaring(OffOrAuto.Off)
         .run();
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 0ca84a1..7a0e793 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -22,29 +22,35 @@
 import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 
-public abstract class RunExamplesAndroidOTest<B> {
+public abstract class RunExamplesAndroidOTest
+      <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
   static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
 
-  abstract class TestRunner {
+  abstract class TestRunner<C extends TestRunner<C>> {
     final String testName;
     final String packageName;
     final String mainClass;
 
+    Integer androidJarVersion = null;
+
     final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
     final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
     final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
@@ -55,20 +61,22 @@
       this.mainClass = mainClass;
     }
 
-    TestRunner withDexCheck(Consumer<DexInspector> check) {
+    abstract C self();
+
+    C withDexCheck(Consumer<DexInspector> check) {
       dexInspectorChecks.add(check);
-      return this;
+      return self();
     }
 
-    TestRunner withClassCheck(Consumer<FoundClassSubject> check) {
+    C withClassCheck(Consumer<FoundClassSubject> check) {
       return withDexCheck(inspector -> inspector.forAllClasses(check));
     }
 
-    TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) {
+    C withMethodCheck(Consumer<FoundMethodSubject> check) {
       return withClassCheck(clazz -> clazz.forAllMethods(check));
     }
 
-    <T extends InstructionSubject> TestRunner withInstructionCheck(
+    <T extends InstructionSubject> C withInstructionCheck(
         Predicate<InstructionSubject> filter, Consumer<T> check) {
       return withMethodCheck(method -> {
         if (method.isAbstract()) {
@@ -81,22 +89,22 @@
       });
     }
 
-    TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+    C withOptionConsumer(Consumer<InternalOptions> consumer) {
       optionConsumers.add(consumer);
-      return this;
+      return self();
     }
 
-    TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+    C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
 
-    TestRunner withTryWithResourcesDesugaring(OffOrAuto behavior) {
+    C withTryWithResourcesDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
     }
 
-    TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+    C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
       builderTransformations.add(builderTransformation);
-      return this;
+      return self();
     }
 
     void combinedOptionConsumer(InternalOptions options) {
@@ -105,13 +113,25 @@
       }
     }
 
+    Path build() throws Throwable {
+      Path inputFile = getInputJar();
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+      return out;
+    }
+
+    Path getInputJar() {
+      return Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+    }
+
     void run() throws Throwable {
       if (minSdkErrorExpected(testName)) {
         thrown.expect(ApiLevelException.class);
       }
 
       String qualifiedMainClass = packageName + "." + mainClass;
-      Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+      Path inputFile = getInputJar();
       Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
 
       build(inputFile, out);
@@ -120,11 +140,6 @@
         return;
       }
 
-      boolean expectedToFail = expectedToFail(testName);
-      if (expectedToFail) {
-        thrown.expect(Throwable.class);
-      }
-
       if (!dexInspectorChecks.isEmpty()) {
         DexInspector inspector = new DexInspector(out);
         for (Consumer<DexInspector> check : dexInspectorChecks) {
@@ -132,21 +147,16 @@
         }
       }
 
-      String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
-      if (!expectedToFail) {
-        ToolHelper.ProcessResult javaResult =
-            ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
-        assertEquals("JVM run failed", javaResult.exitCode, 0);
-        assertTrue(
-            "JVM output does not match art output.\n\tjvm: "
-                + javaResult.stdout
-                + "\n\tart: "
-                + output,
-            output.equals(javaResult.stdout));
-      }
+      execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out});
     }
 
-    abstract TestRunner withMinApiLevel(int minApiLevel);
+    abstract C withMinApiLevel(int minApiLevel);
+
+    C withAndroidJar(int androidJarVersion) {
+      assert this.androidJarVersion == null;
+      this.androidJarVersion = androidJarVersion;
+      return self();
+    }
 
     abstract void build(Path inputFile, Path out) throws Throwable;
   }
@@ -164,7 +174,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_5_1_1, ImmutableList.of(
               // API not supported
@@ -173,7 +188,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_6_0_1, ImmutableList.of(
               // API not supported
@@ -182,7 +202,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_7_0_0, ImmutableList.of(
               // API not supported
@@ -190,7 +215,10 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_DEFAULT, ImmutableList.of(
           )
@@ -266,6 +294,24 @@
   }
 
   @Test
+  public void desugarDefaultMethodInAndroidJar25() throws Throwable {
+    test("DefaultMethodInAndroidJar25", "desugaringwithandroidjar25", "DefaultMethodInAndroidJar25")
+        .withMinApiLevel(ANDROID_K_API)
+        .withAndroidJar(ANDROID_O_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
+  public void desugarStaticMethodInAndroidJar25() throws Throwable {
+    test("StaticMethodInAndroidJar25", "desugaringwithandroidjar25", "StaticMethodInAndroidJar25")
+        .withMinApiLevel(ANDROID_K_API)
+        .withAndroidJar(ANDROID_O_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
   public void lambdaDesugaringValueAdjustments() throws Throwable {
     test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
         .withMinApiLevel(ANDROID_K_API)
@@ -315,5 +361,34 @@
         .run();
   }
 
-  abstract TestRunner test(String testName, String packageName, String mainClass);
+  abstract RunExamplesAndroidOTest<B>.TestRunner<?> test(String testName, String packageName, String mainClass);
+
+  void execute(
+      String testName,
+      String qualifiedMainClass, Path[] jars, Path[] dexes)
+      throws IOException {
+
+    boolean expectedToFail = expectedToFail(testName);
+    if (expectedToFail) {
+      thrown.expect(Throwable.class);
+    }
+    String output = ToolHelper.runArtNoVerificationErrors(
+        Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+        qualifiedMainClass,
+        null);
+    if (!expectedToFail) {
+      ToolHelper.ProcessResult javaResult =
+          ToolHelper.runJava(
+              Arrays.stream(jars).map(path -> path.toString()).collect(Collectors.toList()),
+              qualifiedMainClass);
+      assertEquals("JVM run failed", javaResult.exitCode, 0);
+      assertTrue(
+          "JVM output does not match art output.\n\tjvm: "
+              + javaResult.stdout
+              + "\n\tart: "
+              + output,
+          output.equals(javaResult.stdout));
+    }
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 4f84303..3767286 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,15 +14,11 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index fa78fd0..c89e792 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -135,6 +135,7 @@
             .setOutputPath(dexOutputDir)
             .setMinApiLevel(minSdk)
             .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
             .build(),
         optionsConsumer);
     return dexOutputDir.resolve("classes.dex");
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index b6debff..39d90d6 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -45,14 +45,6 @@
   }
 
   private static Set<String> knownIssues = Sets.newHashSet(new String[]{
-      "espresso-core-3.0.0.jar",
-      "hamcrest-integration-1.3.jar",
-      "hamcrest-library-1.3.jar",
-      "junit-4.12.jar",
-      "support-core-ui-25.4.0.jar",
-      "support-media-compat-25.4.0.jar",
-      "support-fragment-25.4.0.jar",
-      "support-compat-25.4.0.jar"
   });
 
   @Rule
@@ -98,4 +90,18 @@
         .build(),
         options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
   }
+
+  @Test
+  public void testCompileDontDesugarDefault() throws IOException, CompilationException {
+    if (knownIssues.contains(name)) {
+      thrown.expect(CompilationError.class);
+    }
+    ToolHelper.runD8(
+        D8Command.builder().addClasspathFiles(classpath)
+        .addProgramFiles(toCompile)
+        .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(Constants.ANDROID_K_API)))
+        .setMinApiLevel(Constants.ANDROID_K_API)
+        .build(),
+        options -> options.interfaceMethodDesugaring = OffOrAuto.Off);
+  }
 }