Merge commit 'cda44f1ed34560c324a1031f31542d406cced5ae' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 58e68dc..70d4425 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -335,9 +335,7 @@
           // Mark dead proto extensions fields as neither being read nor written. This step must
           // run prior to the tree pruner.
           appView.withGeneratedExtensionRegistryShrinker(
-              shrinker -> {
-                shrinker.run(Mode.INITIAL_TREE_SHAKING);
-              });
+              shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING));
 
           TreePruner pruner = new TreePruner(appViewWithLiveness);
           application = pruner.run(application);
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7a2c325..f327f21 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -43,25 +43,28 @@
   public static class DesugaredLibraryCodeToKeep extends CodeToKeep {
 
     private static class KeepStruct {
+
       Set<DexField> fields = Sets.newConcurrentHashSet();
       Set<DexMethod> methods = Sets.newConcurrentHashSet();
       boolean all = false;
     }
 
     private final NamingLens namingLens;
-    private final Set<DexType> emulatedInterfaces = Sets.newIdentityHashSet();
+    private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
     private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
     private final InternalOptions options;
 
     public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
-      emulatedInterfaces.addAll(
-          options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
       this.namingLens = namingLens;
       this.options = options;
+      potentialTypesToKeep.addAll(
+          options.desugaredLibraryConfiguration.getEmulateLibraryInterface().values());
+      potentialTypesToKeep.addAll(
+          options.desugaredLibraryConfiguration.getCustomConversions().values());
     }
 
     private boolean shouldKeep(DexType type) {
-      return namingLens.prefixRewrittenType(type) != null || emulatedInterfaces.contains(type);
+      return namingLens.prefixRewrittenType(type) != null || potentialTypesToKeep.contains(type);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index f573b76..328bd61 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -16,13 +16,19 @@
 
   DexField getField();
 
+  int getNumberOfWriteContexts();
+
   DexEncodedMethod getUniqueReadContext();
 
   void forEachIndirectAccess(Consumer<DexField> consumer);
 
   void forEachIndirectAccessWithContexts(BiConsumer<DexField, Set<DexEncodedMethod>> consumer);
 
-  void forEachReadContext(Consumer<DexMethod> consumer);
+  void forEachReadContext(Consumer<DexEncodedMethod> consumer);
+
+  void forEachWriteContext(Consumer<DexEncodedMethod> consumer);
+
+  boolean hasReflectiveAccess();
 
   boolean isRead();
 
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
index 1f07f43..5b0ebc2 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
@@ -4,12 +4,17 @@
 
 package com.android.tools.r8.graph;
 
+import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
 /** Provides immutable access to {@link FieldAccessInfoCollectionImpl}. */
 public interface FieldAccessInfoCollection<T extends FieldAccessInfo> {
 
+  void flattenAccessContexts();
+
   T get(DexField field);
 
   void forEach(Consumer<T> consumer);
+
+  void removeIf(BiPredicate<DexField, FieldAccessInfoImpl> predicate);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 3a307cf..47a9c30 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -16,6 +16,11 @@
   private Map<DexField, FieldAccessInfoImpl> infos = new IdentityHashMap<>();
 
   @Override
+  public void flattenAccessContexts() {
+    infos.values().forEach(FieldAccessInfoImpl::flattenAccessContexts);
+  }
+
+  @Override
   public FieldAccessInfoImpl get(DexField field) {
     return infos.get(field);
   }
@@ -32,6 +37,7 @@
     infos.values().forEach(consumer);
   }
 
+  @Override
   public void removeIf(BiPredicate<DexField, FieldAccessInfoImpl> predicate) {
     infos.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
   }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 5924435..99ad00f 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -24,6 +25,9 @@
   // A direct reference to the definition of the field.
   private DexField field;
 
+  // If this field has a reflective access.
+  private boolean hasReflectiveAccess;
+
   // Maps every direct and indirect reference in a read-context to the set of methods in which that
   // reference appears.
   private Map<DexField, Set<DexEncodedMethod>> readsWithContexts;
@@ -36,6 +40,29 @@
     this.field = field;
   }
 
+  void flattenAccessContexts() {
+    flattenAccessContexts(readsWithContexts);
+    flattenAccessContexts(writesWithContexts);
+  }
+
+  private void flattenAccessContexts(Map<DexField, Set<DexEncodedMethod>> accessesWithContexts) {
+    if (accessesWithContexts != null) {
+      Set<DexEncodedMethod> flattenedAccessContexts =
+          accessesWithContexts.computeIfAbsent(field, ignore -> Sets.newIdentityHashSet());
+      accessesWithContexts.forEach(
+          (access, contexts) -> {
+            if (access != field) {
+              flattenedAccessContexts.addAll(contexts);
+            }
+          });
+      accessesWithContexts.clear();
+      if (!flattenedAccessContexts.isEmpty()) {
+        accessesWithContexts.put(field, flattenedAccessContexts);
+      }
+      assert accessesWithContexts.size() <= 1;
+    }
+  }
+
   @Override
   public FieldAccessInfoImpl asMutable() {
     return this;
@@ -47,6 +74,19 @@
   }
 
   @Override
+  public int getNumberOfWriteContexts() {
+    if (writesWithContexts != null) {
+      if (writesWithContexts.size() == 1) {
+        return writesWithContexts.values().iterator().next().size();
+      } else {
+        throw new Unreachable(
+            "Should only be querying the number of write contexts after flattening");
+      }
+    }
+    return 0;
+  }
+
+  @Override
   public DexEncodedMethod getUniqueReadContext() {
     if (readsWithContexts != null && readsWithContexts.size() == 1) {
       Set<DexEncodedMethod> contexts = readsWithContexts.values().iterator().next();
@@ -110,22 +150,41 @@
   }
 
   @Override
-  public void forEachReadContext(Consumer<DexMethod> consumer) {
+  public void forEachReadContext(Consumer<DexEncodedMethod> consumer) {
+    forEachAccessContext(readsWithContexts, consumer);
+  }
+
+  @Override
+  public void forEachWriteContext(Consumer<DexEncodedMethod> consumer) {
+    forEachAccessContext(writesWithContexts, consumer);
+  }
+
+  private void forEachAccessContext(
+      Map<DexField, Set<DexEncodedMethod>> accessesWithContexts,
+      Consumer<DexEncodedMethod> consumer) {
     // There can be indirect reads and writes of the same field reference, so we need to keep track
     // of the previously-seen indirect accesses to avoid reporting duplicates.
-    Set<DexMethod> visited = Sets.newIdentityHashSet();
-    if (readsWithContexts != null) {
-      for (Set<DexEncodedMethod> encodedReadContexts : readsWithContexts.values()) {
-        for (DexEncodedMethod encodedReadContext : encodedReadContexts) {
-          DexMethod readContext = encodedReadContext.method;
-          if (visited.add(readContext)) {
-            consumer.accept(readContext);
+    Set<DexEncodedMethod> visited = Sets.newIdentityHashSet();
+    if (accessesWithContexts != null) {
+      for (Set<DexEncodedMethod> encodedAccessContexts : accessesWithContexts.values()) {
+        for (DexEncodedMethod encodedAccessContext : encodedAccessContexts) {
+          if (visited.add(encodedAccessContext)) {
+            consumer.accept(encodedAccessContext);
           }
         }
       }
     }
   }
 
+  @Override
+  public boolean hasReflectiveAccess() {
+    return hasReflectiveAccess;
+  }
+
+  public void setHasReflectiveAccess() {
+    hasReflectiveAccess = true;
+  }
+
   /** Returns true if this field is read by the program. */
   @Override
   public boolean isRead() {
@@ -208,6 +267,9 @@
 
   public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
+    if (hasReflectiveAccess) {
+      rewritten.setHasReflectiveAccess();
+    }
     if (readsWithContexts != null) {
       rewritten.readsWithContexts = new IdentityHashMap<>();
       readsWithContexts.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 09a9055..afac334 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -3,12 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ir.code.ConstInstruction;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableList;
@@ -23,14 +18,11 @@
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 /**
@@ -73,270 +65,6 @@
     }
   }
 
-  public static class RewrittenPrototypeDescription {
-
-    public static class RemovedArgumentInfo {
-
-      public static class Builder {
-
-        private int argumentIndex = -1;
-        private boolean isAlwaysNull = false;
-        private DexType type = null;
-
-        public Builder setArgumentIndex(int argumentIndex) {
-          this.argumentIndex = argumentIndex;
-          return this;
-        }
-
-        public Builder setIsAlwaysNull() {
-          this.isAlwaysNull = true;
-          return this;
-        }
-
-        public Builder setType(DexType type) {
-          this.type = type;
-          return this;
-        }
-
-        public RemovedArgumentInfo build() {
-          assert argumentIndex >= 0;
-          assert type != null;
-          return new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type);
-        }
-      }
-
-      private final int argumentIndex;
-      private final boolean isAlwaysNull;
-      private final DexType type;
-
-      private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull, DexType type) {
-        this.argumentIndex = argumentIndex;
-        this.isAlwaysNull = isAlwaysNull;
-        this.type = type;
-      }
-
-      public static Builder builder() {
-        return new Builder();
-      }
-
-      public int getArgumentIndex() {
-        return argumentIndex;
-      }
-
-      public DexType getType() {
-        return type;
-      }
-
-      public boolean isAlwaysNull() {
-        return isAlwaysNull;
-      }
-
-      public boolean isNeverUsed() {
-        return !isAlwaysNull;
-      }
-
-      public RemovedArgumentInfo withArgumentIndex(int argumentIndex) {
-        return this.argumentIndex != argumentIndex
-            ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type)
-            : this;
-      }
-    }
-
-    public static class RemovedArgumentsInfo {
-
-      private static final RemovedArgumentsInfo empty = new RemovedArgumentsInfo(null);
-
-      private final List<RemovedArgumentInfo> removedArguments;
-
-      public RemovedArgumentsInfo(List<RemovedArgumentInfo> removedArguments) {
-        assert verifyRemovedArguments(removedArguments);
-        this.removedArguments = removedArguments;
-      }
-
-      private static boolean verifyRemovedArguments(List<RemovedArgumentInfo> removedArguments) {
-        if (removedArguments != null && !removedArguments.isEmpty()) {
-          // Check that list is sorted by argument indices.
-          int lastArgumentIndex = removedArguments.get(0).getArgumentIndex();
-          for (int i = 1; i < removedArguments.size(); ++i) {
-            int currentArgumentIndex = removedArguments.get(i).getArgumentIndex();
-            assert lastArgumentIndex < currentArgumentIndex;
-            lastArgumentIndex = currentArgumentIndex;
-          }
-        }
-        return true;
-      }
-
-      public static RemovedArgumentsInfo empty() {
-        return empty;
-      }
-
-      public ListIterator<RemovedArgumentInfo> iterator() {
-        return removedArguments == null
-            ? Collections.emptyListIterator()
-            : removedArguments.listIterator();
-      }
-
-      public boolean hasRemovedArguments() {
-        return removedArguments != null && !removedArguments.isEmpty();
-      }
-
-      public boolean isArgumentRemoved(int argumentIndex) {
-        if (removedArguments != null) {
-          for (RemovedArgumentInfo info : removedArguments) {
-            if (info.getArgumentIndex() == argumentIndex) {
-              return true;
-            }
-          }
-        }
-        return false;
-      }
-
-      public int numberOfRemovedArguments() {
-        return removedArguments != null ? removedArguments.size() : 0;
-      }
-
-      public RemovedArgumentsInfo combine(RemovedArgumentsInfo info) {
-        assert info != null;
-        if (hasRemovedArguments()) {
-          if (!info.hasRemovedArguments()) {
-            return this;
-          }
-        } else {
-          return info;
-        }
-
-        List<RemovedArgumentInfo> newRemovedArguments = new LinkedList<>(removedArguments);
-        ListIterator<RemovedArgumentInfo> iterator = newRemovedArguments.listIterator();
-        int offset = 0;
-        for (RemovedArgumentInfo pending : info.removedArguments) {
-          RemovedArgumentInfo next = IteratorUtils.peekNext(iterator);
-          while (next != null && next.getArgumentIndex() <= pending.getArgumentIndex() + offset) {
-            iterator.next();
-            next = IteratorUtils.peekNext(iterator);
-            offset++;
-          }
-          iterator.add(pending.withArgumentIndex(pending.getArgumentIndex() + offset));
-        }
-        return new RemovedArgumentsInfo(newRemovedArguments);
-      }
-
-      public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
-          DexEncodedMethod method) {
-        if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
-          return builder -> {
-            int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
-            builder.removeParameterAnnotations(
-                oldIndex -> isArgumentRemoved(oldIndex + firstArgumentIndex));
-          };
-        }
-        return null;
-      }
-    }
-
-    private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
-
-    private final boolean hasBeenChangedToReturnVoid;
-    private final boolean extraNullParameter;
-    private final RemovedArgumentsInfo removedArgumentsInfo;
-
-    private RewrittenPrototypeDescription() {
-      this(false, false, RemovedArgumentsInfo.empty());
-    }
-
-    public RewrittenPrototypeDescription(
-        boolean hasBeenChangedToReturnVoid,
-        boolean extraNullParameter,
-        RemovedArgumentsInfo removedArgumentsInfo) {
-      assert removedArgumentsInfo != null;
-      this.extraNullParameter = extraNullParameter;
-      this.hasBeenChangedToReturnVoid = hasBeenChangedToReturnVoid;
-      this.removedArgumentsInfo = removedArgumentsInfo;
-    }
-
-    public static RewrittenPrototypeDescription none() {
-      return none;
-    }
-
-    public boolean isEmpty() {
-      return !extraNullParameter
-          && !hasBeenChangedToReturnVoid
-          && !getRemovedArgumentsInfo().hasRemovedArguments();
-    }
-
-    public boolean hasExtraNullParameter() {
-      return extraNullParameter;
-    }
-
-    public boolean hasBeenChangedToReturnVoid() {
-      return hasBeenChangedToReturnVoid;
-    }
-
-    public RemovedArgumentsInfo getRemovedArgumentsInfo() {
-      return removedArgumentsInfo;
-    }
-
-    /**
-     * Returns the {@link ConstInstruction} that should be used to materialize the result of
-     * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
-     *
-     * <p>This method should only be used for methods that return a constant value and whose return
-     * type has been changed to void.
-     *
-     * <p>Note that the current implementation always returns null at this point.
-     */
-    public ConstInstruction getConstantReturn(IRCode code, Position position) {
-      assert hasBeenChangedToReturnVoid;
-      ConstInstruction instruction = code.createConstNull();
-      instruction.setPosition(position);
-      return instruction;
-    }
-
-    public DexType rewriteReturnType(DexType returnType, DexItemFactory dexItemFactory) {
-      return hasBeenChangedToReturnVoid ? dexItemFactory.voidType : returnType;
-    }
-
-    public DexType[] rewriteParameters(DexType[] params) {
-      RemovedArgumentsInfo removedArgumentsInfo = getRemovedArgumentsInfo();
-      if (removedArgumentsInfo.hasRemovedArguments()) {
-        DexType[] newParams =
-            new DexType[params.length - removedArgumentsInfo.numberOfRemovedArguments()];
-        int newParamIndex = 0;
-        for (int oldParamIndex = 0; oldParamIndex < params.length; ++oldParamIndex) {
-          if (!removedArgumentsInfo.isArgumentRemoved(oldParamIndex)) {
-            newParams[newParamIndex] = params[oldParamIndex];
-            ++newParamIndex;
-          }
-        }
-        return newParams;
-      }
-      return params;
-    }
-
-    public DexProto rewriteProto(DexProto proto, DexItemFactory dexItemFactory) {
-      DexType newReturnType = rewriteReturnType(proto.returnType, dexItemFactory);
-      DexType[] newParameters = rewriteParameters(proto.parameters.values);
-      return dexItemFactory.createProto(newReturnType, newParameters);
-    }
-
-    public RewrittenPrototypeDescription withConstantReturn() {
-      return !hasBeenChangedToReturnVoid
-          ? new RewrittenPrototypeDescription(true, extraNullParameter, removedArgumentsInfo)
-          : this;
-    }
-
-    public RewrittenPrototypeDescription withRemovedArguments(RemovedArgumentsInfo other) {
-      return new RewrittenPrototypeDescription(
-          hasBeenChangedToReturnVoid, extraNullParameter, removedArgumentsInfo.combine(other));
-    }
-
-    public RewrittenPrototypeDescription withExtraNullParameter() {
-      return !extraNullParameter
-          ? new RewrittenPrototypeDescription(
-              hasBeenChangedToReturnVoid, true, removedArgumentsInfo)
-          : this;
-    }
-  }
-
   public static class Builder {
 
     protected Builder() {}
@@ -594,7 +322,7 @@
         if (isContextFreeForMethod(method)) {
           result.put(lookupMethod(method), entry.getBooleanValue());
         } else {
-          for (DexMethod candidate: lookupMethodInAllContexts(method)) {
+          for (DexMethod candidate : lookupMethodInAllContexts(method)) {
             result.put(candidate, entry.getBooleanValue());
           }
         }
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 23f2a61..a25bab3 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -389,12 +389,7 @@
       //     public void bar() { }
       //   }
       //
-      if (resolvedMethod.hasCode()) {
-        DexProgramClass holder = resolvedHolder.asProgramClass();
-        if (appInfo.hasAnyInstantiatedLambdas(holder)) {
-          result.add(resolvedMethod);
-        }
-      }
+      addIfDefaultMethodWithLambdaInstantiations(appInfo, resolvedMethod, result);
 
       DexMethod method = resolvedMethod.method;
       Consumer<DexEncodedMethod> addIfNotAbstract =
@@ -404,10 +399,8 @@
             }
           };
       // Default methods are looked up when looking at a specific subtype that does not override
-      // them.
-      // Otherwise, we would look up default methods that are actually never used. However, we have
-      // to
-      // add bridge methods, otherwise we can remove a bridge that will be used.
+      // them. Otherwise, we would look up default methods that are actually never used. However, we
+      // have to add bridge methods, otherwise we can remove a bridge that will be used.
       Consumer<DexEncodedMethod> addIfNotAbstractAndBridge =
           m -> {
             if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) {
@@ -422,7 +415,11 @@
         if (clazz.isInterface()) {
           ResolutionResult targetMethods = appInfo.resolveMethodOnInterface(clazz, method);
           if (targetMethods.isSingleResolution()) {
-            addIfNotAbstractAndBridge.accept(targetMethods.getSingleTarget());
+            // Sub-interfaces can have default implementations that override the resolved method.
+            // Therefore we have to add default methods in sub interfaces.
+            DexEncodedMethod singleTarget = targetMethods.getSingleTarget();
+            addIfDefaultMethodWithLambdaInstantiations(appInfo, singleTarget, result);
+            addIfNotAbstractAndBridge.accept(singleTarget);
           }
         } else {
           ResolutionResult targetMethods = appInfo.resolveMethodOnClass(clazz, method);
@@ -433,6 +430,19 @@
       }
       return result;
     }
+
+    private void addIfDefaultMethodWithLambdaInstantiations(
+        AppInfoWithSubtyping appInfo, DexEncodedMethod method, Set<DexEncodedMethod> result) {
+      if (method == null) {
+        return;
+      }
+      if (method.hasCode()) {
+        DexProgramClass holder = appInfo.definitionForProgramType(method.method.holder);
+        if (appInfo.hasAnyInstantiatedLambdas(holder)) {
+          result.add(method);
+        }
+      }
+    }
   }
 
   abstract static class EmptyResult extends ResolutionResult {
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
new file mode 100644
index 0000000..5c22878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -0,0 +1,289 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ir.code.ConstInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IteratorUtils;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Consumer;
+
+public class RewrittenPrototypeDescription {
+
+  public static class RemovedArgumentInfo {
+
+    public static class Builder {
+
+      private int argumentIndex = -1;
+      private boolean isAlwaysNull = false;
+      private DexType type = null;
+
+      public Builder setArgumentIndex(int argumentIndex) {
+        this.argumentIndex = argumentIndex;
+        return this;
+      }
+
+      public Builder setIsAlwaysNull() {
+        this.isAlwaysNull = true;
+        return this;
+      }
+
+      public Builder setType(DexType type) {
+        this.type = type;
+        return this;
+      }
+
+      public RemovedArgumentInfo build() {
+        assert argumentIndex >= 0;
+        assert type != null;
+        return new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type);
+      }
+    }
+
+    private final int argumentIndex;
+    private final boolean isAlwaysNull;
+    private final DexType type;
+
+    private RemovedArgumentInfo(int argumentIndex, boolean isAlwaysNull, DexType type) {
+      this.argumentIndex = argumentIndex;
+      this.isAlwaysNull = isAlwaysNull;
+      this.type = type;
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public int getArgumentIndex() {
+      return argumentIndex;
+    }
+
+    public DexType getType() {
+      return type;
+    }
+
+    public boolean isAlwaysNull() {
+      return isAlwaysNull;
+    }
+
+    public boolean isNeverUsed() {
+      return !isAlwaysNull;
+    }
+
+    RemovedArgumentInfo withArgumentIndex(int argumentIndex) {
+      return this.argumentIndex != argumentIndex
+          ? new RemovedArgumentInfo(argumentIndex, isAlwaysNull, type)
+          : this;
+    }
+  }
+
+  public static class RemovedArgumentsInfo {
+
+    private static final RemovedArgumentsInfo empty = new RemovedArgumentsInfo(null);
+
+    private final List<RemovedArgumentInfo> removedArguments;
+
+    public RemovedArgumentsInfo(List<RemovedArgumentInfo> removedArguments) {
+      assert verifyRemovedArguments(removedArguments);
+      this.removedArguments = removedArguments;
+    }
+
+    private static boolean verifyRemovedArguments(List<RemovedArgumentInfo> removedArguments) {
+      if (removedArguments != null && !removedArguments.isEmpty()) {
+        // Check that list is sorted by argument indices.
+        int lastArgumentIndex = removedArguments.get(0).getArgumentIndex();
+        for (int i = 1; i < removedArguments.size(); ++i) {
+          int currentArgumentIndex = removedArguments.get(i).getArgumentIndex();
+          assert lastArgumentIndex < currentArgumentIndex;
+          lastArgumentIndex = currentArgumentIndex;
+        }
+      }
+      return true;
+    }
+
+    public static RemovedArgumentsInfo empty() {
+      return empty;
+    }
+
+    public ListIterator<RemovedArgumentInfo> iterator() {
+      return removedArguments == null
+          ? Collections.emptyListIterator()
+          : removedArguments.listIterator();
+    }
+
+    public boolean hasRemovedArguments() {
+      return removedArguments != null && !removedArguments.isEmpty();
+    }
+
+    public boolean isArgumentRemoved(int argumentIndex) {
+      if (removedArguments != null) {
+        for (RemovedArgumentInfo info : removedArguments) {
+          if (info.getArgumentIndex() == argumentIndex) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+
+    public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
+      // Currently not allowed to remove the receiver of an instance method. This would involve
+      // changing invoke-direct/invoke-virtual into invoke-static.
+      assert encodedMethod.isStatic() || !isArgumentRemoved(0);
+      DexType[] params = encodedMethod.method.proto.parameters.values;
+      if (!hasRemovedArguments()) {
+        return params;
+      }
+      DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
+      int offset = encodedMethod.isStatic() ? 0 : 1;
+      int newParamIndex = 0;
+      for (int oldParamIndex = 0; oldParamIndex < params.length; ++oldParamIndex) {
+        if (!isArgumentRemoved(oldParamIndex + offset)) {
+          newParams[newParamIndex++] = params[oldParamIndex];
+        }
+      }
+      return newParams;
+    }
+
+    public int numberOfRemovedArguments() {
+      return removedArguments != null ? removedArguments.size() : 0;
+    }
+
+    public RemovedArgumentsInfo combine(RemovedArgumentsInfo info) {
+      assert info != null;
+      if (hasRemovedArguments()) {
+        if (!info.hasRemovedArguments()) {
+          return this;
+        }
+      } else {
+        return info;
+      }
+
+      List<RemovedArgumentInfo> newRemovedArguments = new LinkedList<>(removedArguments);
+      ListIterator<RemovedArgumentInfo> iterator = newRemovedArguments.listIterator();
+      int offset = 0;
+      for (RemovedArgumentInfo pending : info.removedArguments) {
+        RemovedArgumentInfo next = IteratorUtils.peekNext(iterator);
+        while (next != null && next.getArgumentIndex() <= pending.getArgumentIndex() + offset) {
+          iterator.next();
+          next = IteratorUtils.peekNext(iterator);
+          offset++;
+        }
+        iterator.add(pending.withArgumentIndex(pending.getArgumentIndex() + offset));
+      }
+      return new RemovedArgumentsInfo(newRemovedArguments);
+    }
+
+    public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+        DexEncodedMethod method) {
+      if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
+        return builder -> {
+          int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
+          builder.removeParameterAnnotations(
+              oldIndex -> isArgumentRemoved(oldIndex + firstArgumentIndex));
+        };
+      }
+      return null;
+    }
+  }
+
+  private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
+
+  private final boolean hasBeenChangedToReturnVoid;
+  private final boolean extraNullParameter;
+  private final RemovedArgumentsInfo removedArgumentsInfo;
+
+  private RewrittenPrototypeDescription() {
+    this(false, false, RemovedArgumentsInfo.empty());
+  }
+
+  private RewrittenPrototypeDescription(
+      boolean hasBeenChangedToReturnVoid,
+      boolean extraNullParameter,
+      RemovedArgumentsInfo removedArgumentsInfo) {
+    assert removedArgumentsInfo != null;
+    this.extraNullParameter = extraNullParameter;
+    this.hasBeenChangedToReturnVoid = hasBeenChangedToReturnVoid;
+    this.removedArgumentsInfo = removedArgumentsInfo;
+  }
+
+  public static RewrittenPrototypeDescription createForUninstantiatedTypes(
+      boolean hasBeenChangedToReturnVoid, RemovedArgumentsInfo removedArgumentsInfo) {
+    return new RewrittenPrototypeDescription(
+        hasBeenChangedToReturnVoid, false, removedArgumentsInfo);
+  }
+
+  public static RewrittenPrototypeDescription none() {
+    return none;
+  }
+
+  public boolean isEmpty() {
+    return !extraNullParameter
+        && !hasBeenChangedToReturnVoid
+        && !getRemovedArgumentsInfo().hasRemovedArguments();
+  }
+
+  public boolean hasExtraNullParameter() {
+    return extraNullParameter;
+  }
+
+  public boolean hasBeenChangedToReturnVoid() {
+    return hasBeenChangedToReturnVoid;
+  }
+
+  public RemovedArgumentsInfo getRemovedArgumentsInfo() {
+    return removedArgumentsInfo;
+  }
+
+  /**
+   * Returns the {@link ConstInstruction} that should be used to materialize the result of
+   * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
+   *
+   * <p>This method should only be used for methods that return a constant value and whose return
+   * type has been changed to void.
+   *
+   * <p>Note that the current implementation always returns null at this point.
+   */
+  public ConstInstruction getConstantReturn(IRCode code, Position position) {
+    assert hasBeenChangedToReturnVoid;
+    ConstInstruction instruction = code.createConstNull();
+    instruction.setPosition(position);
+    return instruction;
+  }
+
+  public DexProto rewriteProto(DexEncodedMethod encodedMethod, DexItemFactory dexItemFactory) {
+    if (isEmpty()) {
+      return encodedMethod.method.proto;
+    }
+    DexType newReturnType =
+        hasBeenChangedToReturnVoid
+            ? dexItemFactory.voidType
+            : encodedMethod.method.proto.returnType;
+    DexType[] newParameters = removedArgumentsInfo.rewriteParameters(encodedMethod);
+    return dexItemFactory.createProto(newReturnType, newParameters);
+  }
+
+  public RewrittenPrototypeDescription withConstantReturn() {
+    return !hasBeenChangedToReturnVoid
+        ? new RewrittenPrototypeDescription(true, extraNullParameter, removedArgumentsInfo)
+        : this;
+  }
+
+  public RewrittenPrototypeDescription withRemovedArguments(RemovedArgumentsInfo other) {
+    return new RewrittenPrototypeDescription(
+        hasBeenChangedToReturnVoid, extraNullParameter, removedArgumentsInfo.combine(other));
+  }
+
+  public RewrittenPrototypeDescription withExtraNullParameter() {
+    return !extraNullParameter
+        ? new RewrittenPrototypeDescription(hasBeenChangedToReturnVoid, true, removedArgumentsInfo)
+        : this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
new file mode 100644
index 0000000..b463c75
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class FieldAccessAnalysis {
+
+  private final AppView<?> appView;
+  private final FieldAssignmentTracker fieldAssignmentTracker;
+  private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+
+  public FieldAccessAnalysis(AppView<AppInfoWithLiveness> appView) {
+    InternalOptions options = appView.options();
+    this.appView = appView;
+    this.fieldBitAccessAnalysis =
+        options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
+    this.fieldAssignmentTracker =
+        options.enableFieldAssignmentTracker ? new FieldAssignmentTracker(appView) : null;
+  }
+
+  public FieldAccessAnalysis(
+      AppView<?> appView,
+      FieldAssignmentTracker fieldAssignmentTracker,
+      FieldBitAccessAnalysis fieldBitAccessAnalysis) {
+    this.appView = appView;
+    this.fieldAssignmentTracker = fieldAssignmentTracker;
+    this.fieldBitAccessAnalysis = fieldBitAccessAnalysis;
+  }
+
+  public static boolean enable(InternalOptions options) {
+    return options.enableFieldBitAccessAnalysis || options.enableFieldAssignmentTracker;
+  }
+
+  public FieldAssignmentTracker fieldAssignmentTracker() {
+    return fieldAssignmentTracker;
+  }
+
+  public void acceptClassInitializerDefaultsResult(
+      ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+    if (fieldAssignmentTracker != null) {
+      fieldAssignmentTracker.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
+    }
+  }
+
+  public void recordFieldAccesses(
+      IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
+    if (!code.metadata().mayHaveFieldInstruction() || !methodProcessor.isPrimary()) {
+      return;
+    }
+
+    Iterable<FieldInstruction> fieldInstructions =
+        code.instructions(Instruction::isFieldInstruction);
+    for (FieldInstruction fieldInstruction : fieldInstructions) {
+      DexEncodedField encodedField = appView.appInfo().resolveField(fieldInstruction.getField());
+      if (encodedField != null && encodedField.isProgramField(appView)) {
+        if (fieldAssignmentTracker != null) {
+          fieldAssignmentTracker.recordFieldAccess(fieldInstruction, encodedField, code.method);
+        }
+        if (fieldBitAccessAnalysis != null) {
+          fieldBitAccessAnalysis.recordFieldAccess(fieldInstruction, encodedField, feedback);
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
new file mode 100644
index 0000000..0b0b542
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class FieldAssignmentTracker {
+
+  private final AppView<AppInfoWithLiveness> appView;
+
+  // A field access graph with edges from methods to the fields that they access. Edges are removed
+  // from the graph as we process methods, such that we can conclude that all field writes have been
+  // processed when a field no longer has any incoming edges.
+  private final FieldAccessGraph fieldAccessGraph;
+
+  // The set of fields that may store a non-zero value.
+  private final Set<DexEncodedField> nonZeroFields = Sets.newConcurrentHashSet();
+
+  FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
+    this.appView = appView;
+    this.fieldAccessGraph = new FieldAccessGraph(appView);
+  }
+
+  private boolean isAlwaysZero(DexEncodedField field) {
+    return !appView.appInfo().isPinned(field.field) && !nonZeroFields.contains(field);
+  }
+
+  void acceptClassInitializerDefaultsResult(
+      ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+    classInitializerDefaultsResult.forEachOptimizedField(
+        (field, value) -> {
+          if (!value.isDefault(field.field.type)) {
+            nonZeroFields.add(field);
+          }
+        });
+  }
+
+  void recordFieldAccess(
+      FieldInstruction instruction, DexEncodedField field, DexEncodedMethod context) {
+    if (instruction.isFieldPut()) {
+      recordFieldPut(field, instruction.value(), context);
+    }
+  }
+
+  private void recordFieldPut(DexEncodedField field, Value value, DexEncodedMethod context) {
+    assert verifyValueIsConsistentWithFieldOptimizationInfo(
+        value, field.getOptimizationInfo(), context);
+    if (!value.isZero()) {
+      nonZeroFields.add(field);
+    }
+  }
+
+  private void recordAllFieldPutsProcessed(DexEncodedField field, OptimizationFeedback feedback) {
+    if (isAlwaysZero(field)) {
+      feedback.recordFieldHasAbstractValue(
+          field, appView, appView.abstractValueFactory().createSingleNumberValue(0));
+    }
+  }
+
+  public void waveDone(Collection<DexEncodedMethod> wave, OptimizationFeedback feedback) {
+    for (DexEncodedMethod method : wave) {
+      fieldAccessGraph.markProcessed(method, field -> recordAllFieldPutsProcessed(field, feedback));
+    }
+  }
+
+  private boolean verifyValueIsConsistentWithFieldOptimizationInfo(
+      Value value, FieldOptimizationInfo optimizationInfo, DexEncodedMethod context) {
+    AbstractValue abstractValue = optimizationInfo.getAbstractValue();
+    if (abstractValue.isUnknown()) {
+      return true;
+    }
+    assert abstractValue == value.getAbstractValue(appView, context.method.holder);
+    return true;
+  }
+
+  static class FieldAccessGraph {
+
+    // The fields written by each method.
+    private final Map<DexEncodedMethod, List<DexEncodedField>> fieldWrites =
+        new IdentityHashMap<>();
+
+    // The number of writes that have not yet been processed per field.
+    private final Reference2IntMap<DexEncodedField> pendingFieldWrites =
+        new Reference2IntOpenHashMap<>();
+
+    FieldAccessGraph(AppView<AppInfoWithLiveness> appView) {
+      FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+          appView.appInfo().getFieldAccessInfoCollection();
+      fieldAccessInfoCollection.flattenAccessContexts();
+      fieldAccessInfoCollection.forEach(
+          info -> {
+            DexEncodedField field = appView.appInfo().resolveField(info.getField());
+            if (field == null) {
+              assert false;
+              return;
+            }
+            if (!info.hasReflectiveAccess()) {
+              info.forEachWriteContext(
+                  context ->
+                      fieldWrites.computeIfAbsent(context, ignore -> new ArrayList<>()).add(field));
+              pendingFieldWrites.put(field, info.getNumberOfWriteContexts());
+            }
+          });
+    }
+
+    void markProcessed(DexEncodedMethod method, Consumer<DexEncodedField> allWritesSeenConsumer) {
+      List<DexEncodedField> fieldWritesInMethod = fieldWrites.get(method);
+      if (fieldWritesInMethod != null) {
+        for (DexEncodedField field : fieldWritesInMethod) {
+          int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field) - 1;
+          if (numberOfPendingFieldWrites > 0) {
+            pendingFieldWrites.put(field, numberOfPendingFieldWrites);
+          } else {
+            allWritesSeenConsumer.accept(field);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
index deabda7..a178a69 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessAnalysis.java
@@ -4,13 +4,9 @@
 
 package com.android.tools.r8.ir.analysis.fieldaccess;
 
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.ir.code.And;
 import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.LogicalBinop;
 import com.android.tools.r8.ir.code.Value;
@@ -19,39 +15,18 @@
 
 public class FieldBitAccessAnalysis {
 
-  private final AppView<? extends AppInfoWithSubtyping> appView;
-
-  public FieldBitAccessAnalysis(AppView<? extends AppInfoWithSubtyping> appView) {
-    assert appView.enableWholeProgramOptimizations();
-    this.appView = appView;
-  }
-
-  public void recordFieldAccesses(IRCode code, OptimizationFeedback feedback) {
-    if (!code.metadata().mayHaveFieldInstruction()) {
+  public void recordFieldAccess(
+      FieldInstruction instruction, DexEncodedField field, OptimizationFeedback feedback) {
+    if (!field.field.type.isIntType()) {
       return;
     }
 
-    for (Instruction instruction : code.instructions()) {
-      if (instruction.isFieldInstruction()) {
-        FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-        DexField field = fieldInstruction.getField();
-        if (!field.type.isIntType()) {
-          continue;
-        }
+    if (BitAccessInfo.allBitsRead(field.getOptimizationInfo().getReadBits())) {
+      return;
+    }
 
-        DexEncodedField encodedField = appView.appInfo().resolveField(field);
-        if (encodedField == null || !encodedField.isProgramField(appView)) {
-          continue;
-        }
-
-        if (BitAccessInfo.allBitsRead(encodedField.getOptimizationInfo().getReadBits())) {
-          continue;
-        }
-
-        if (fieldInstruction.isFieldGet()) {
-          feedback.markFieldBitsRead(encodedField, computeBitsRead(fieldInstruction, encodedField));
-        }
-      }
+    if (instruction.isFieldGet()) {
+      feedback.markFieldBitsRead(field, computeBitsRead(instruction, field));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 0b4bb94..4de5c39 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -99,6 +99,7 @@
    */
   public TreePrunerConfiguration run(Enqueuer.Mode mode) {
     forEachDeadProtoExtensionField(this::recordDeadProtoExtensionField);
+    appView.appInfo().getFieldAccessInfoCollection().removeIf((field, info) -> wasRemoved(field));
     return createTreePrunerConfiguration(mode);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 820789a..4ff6283 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -350,13 +350,13 @@
             // (i) optimize field reads into loading the default value of the field or (ii) remove
             // field writes to proto fields that could be read using reflection by the proto
             // library.
-            enqueuer.registerFieldAccess(valueStorage.field, dynamicMethod);
+            enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
           }
           valueStorageIsLive = true;
         } else if (reachesMapOrRequiredField(protoFieldInfo)) {
           // Map/required fields cannot be removed. Therefore, we mark such fields as both read and
           // written such that we cannot optimize any field reads or writes.
-          enqueuer.registerFieldAccess(valueStorage.field, dynamicMethod);
+          enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
           worklist.enqueueMarkReachableFieldAction(
               clazz, valueStorage, KeepReason.reflectiveUseIn(dynamicMethod));
           valueStorageIsLive = true;
@@ -411,12 +411,12 @@
               writer -> writer != defaultInitializer && writer != dynamicMethod;
           if (enqueuer.isFieldWrittenInMethodSatisfying(
               newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
-            enqueuer.registerFieldRead(newlyLiveField.field, dynamicMethod);
+            enqueuer.registerReflectiveFieldRead(newlyLiveField.field, dynamicMethod);
           }
 
           // Unconditionally register the hazzer and one-of proto fields as written from
           // dynamicMethod().
-          if (enqueuer.registerFieldWrite(newlyLiveField.field, dynamicMethod)) {
+          if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.field, dynamicMethod)) {
             worklist.enqueueMarkReachableFieldAction(
                 clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod));
           }
@@ -527,7 +527,7 @@
       return;
     }
 
-    if (enqueuer.registerFieldWrite(encodedOneOfField.field, dynamicMethod)) {
+    if (enqueuer.registerReflectiveFieldWrite(encodedOneOfField.field, dynamicMethod)) {
       worklist.enqueueMarkReachableFieldAction(
           clazz.asProgramClass(), encodedOneOfField, KeepReason.reflectiveUseIn(dynamicMethod));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index 2bd87e0..c70ff33 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -8,6 +8,10 @@
 
   public abstract boolean isNonTrivial();
 
+  public boolean isZero() {
+    return false;
+  }
+
   /**
    * Returns true if this abstract value represents a single concrete value (i.e., the
    * concretization of this abstract value has size 1).
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 9a43d23..4a5ad60 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -25,6 +25,11 @@
   }
 
   @Override
+  public boolean isZero() {
+    return value == 0;
+  }
+
+  @Override
   public boolean isSingleNumberValue() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 2e5467c..01dcf5d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -40,8 +40,8 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.CanonicalPositions;
@@ -158,7 +158,8 @@
       int register,
       DexEncodedMethod method,
       BiConsumer<Integer, DexType> writeCallback) {
-    RemovedArgumentsInfo removedArgumentsInfo = builder.prototypeChanges.getRemovedArgumentsInfo();
+    RemovedArgumentsInfo removedArgumentsInfo =
+        builder.getPrototypeChanges().getRemovedArgumentsInfo();
     ListIterator<RemovedArgumentInfo> removedArgumentIterator = removedArgumentsInfo.iterator();
     RemovedArgumentInfo nextRemovedArgument =
         removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
@@ -186,7 +187,7 @@
         type =
             TypeLatticeElement.fromDexType(
                 nextRemovedArgument.getType(), Nullability.maybeNull(), builder.appView);
-        builder.addConstantOrUnusedArgument(register);
+        builder.addConstantOrUnusedArgument(register, nextRemovedArgument);
         nextRemovedArgument =
             removedArgumentIterator.hasNext() ? removedArgumentIterator.next() : null;
       } else {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 42728d4..3d0fed3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -34,8 +34,8 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -113,7 +113,6 @@
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -133,7 +132,6 @@
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
@@ -392,9 +390,7 @@
   private DexEncodedMethod context;
   public final AppView<?> appView;
   private final Origin origin;
-  final RewrittenPrototypeDescription prototypeChanges;
-  private ListIterator<RemovedArgumentInfo> removedArgumentsIterator;
-  private int argumentCount = 0;
+  private final RewrittenPrototypeDescription prototypeChanges;
   private Value receiverValue;
   private List<Value> argumentValues;
 
@@ -461,7 +457,6 @@
                 + method.toSourceString());
       }
     }
-    this.removedArgumentsIterator = this.prototypeChanges.getRemovedArgumentsInfo().iterator();
   }
 
   private static boolean verifyMethodSignature(DexEncodedMethod method, GraphLense graphLense) {
@@ -476,6 +471,10 @@
     return method;
   }
 
+  public RewrittenPrototypeDescription getPrototypeChanges() {
+    return prototypeChanges;
+  }
+
   public boolean isDebugMode() {
     return appView.options().debug || method.getOptimizationInfo().isReachabilitySensitive();
   }
@@ -854,16 +853,6 @@
     addInstruction(ir);
   }
 
-  private RemovedArgumentInfo getRemovedArgumentInfo() {
-    RemovedArgumentInfo removedArgumentInfo = IteratorUtils.peekNext(removedArgumentsIterator);
-    if (removedArgumentInfo != null && removedArgumentInfo.getArgumentIndex() == argumentCount) {
-      argumentCount++;
-      return removedArgumentsIterator.next();
-    }
-    argumentCount++;
-    return null;
-  }
-
   void addThisArgument(int register) {
     boolean receiverCouldBeNull = context != null && context != method;
     Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull();
@@ -873,8 +862,6 @@
   }
 
   public void addThisArgument(int register, TypeLatticeElement receiverType) {
-    RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
-    assert removedArgumentInfo == null; // Removal of receiver not yet supported.
     DebugLocalInfo local = getOutgoingLocal(register);
     Value value = writeRegister(register, receiverType, ThrowingInfo.NO_THROW, local);
     addInstruction(new Argument(value, false));
@@ -883,25 +870,15 @@
   }
 
   public void addNonThisArgument(int register, TypeLatticeElement typeLattice) {
-    RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
-    if (removedArgumentInfo == null) {
       DebugLocalInfo local = getOutgoingLocal(register);
       Value value = writeRegister(register, typeLattice, ThrowingInfo.NO_THROW, local);
       addNonThisArgument(new Argument(value, false));
-    } else {
-      handleConstantOrUnusedArgument(register, removedArgumentInfo);
-    }
   }
 
   public void addBooleanNonThisArgument(int register) {
-    RemovedArgumentInfo removedArgumentInfo = getRemovedArgumentInfo();
-    if (removedArgumentInfo == null) {
       DebugLocalInfo local = getOutgoingLocal(register);
       Value value = writeRegister(register, getInt(), ThrowingInfo.NO_THROW, local);
       addNonThisArgument(new Argument(value, true));
-    } else {
-      assert removedArgumentInfo.isNeverUsed();
-    }
   }
 
   private void addNonThisArgument(Argument argument) {
@@ -912,8 +889,8 @@
     argumentValues.add(argument.outValue());
   }
 
-  public void addConstantOrUnusedArgument(int register) {
-    handleConstantOrUnusedArgument(register, getRemovedArgumentInfo());
+  public void addConstantOrUnusedArgument(int register, RemovedArgumentInfo info) {
+    handleConstantOrUnusedArgument(register, info);
   }
 
   private void handleConstantOrUnusedArgument(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 2c9a53c..7800639 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -26,7 +26,7 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.analysis.TypeChecker;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
-import com.android.tools.r8.ir.analysis.fieldaccess.FieldBitAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.FieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.AlwaysMaterializingDefinition;
@@ -128,7 +128,7 @@
   private final Timing timing;
   private final Outliner outliner;
   private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
-  private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+  private final FieldAccessAnalysis fieldAccessAnalysis;
   private final LibraryMethodOptimizer libraryMethodOptimizer;
   private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
   private final StringConcatRewriter stringConcatRewriter;
@@ -232,7 +232,7 @@
       this.dynamicTypeOptimization = null;
       this.classInliner = null;
       this.classStaticizer = null;
-      this.fieldBitAccessAnalysis = null;
+      this.fieldAccessAnalysis = null;
       this.libraryMethodOptimizer = null;
       this.libraryMethodOverrideAnalysis = null;
       this.inliner = null;
@@ -290,10 +290,8 @@
       if (dynamicTypeOptimization != null) {
         assumers.add(dynamicTypeOptimization);
       }
-      this.fieldBitAccessAnalysis =
-          options.enableFieldBitAccessAnalysis
-              ? new FieldBitAccessAnalysis(appViewWithLiveness)
-              : null;
+      this.fieldAccessAnalysis =
+          FieldAccessAnalysis.enable(options) ? new FieldAccessAnalysis(appViewWithLiveness) : null;
       this.libraryMethodOptimizer = new LibraryMethodOptimizer(appViewWithLiveness);
       this.libraryMethodOverrideAnalysis =
           options.enableTreeShakingOfLibraryMethodOverrides
@@ -331,7 +329,7 @@
       this.classInliner = null;
       this.classStaticizer = null;
       this.dynamicTypeOptimization = null;
-      this.fieldBitAccessAnalysis = null;
+      this.fieldAccessAnalysis = null;
       this.libraryMethodOptimizer = null;
       this.libraryMethodOverrideAnalysis = null;
       this.inliner = null;
@@ -805,12 +803,14 @@
     return builder.build();
   }
 
-  private void waveStart(Collection<DexEncodedMethod> wave, ExecutorService executorService)
-      throws ExecutionException {
+  private void waveStart(Collection<DexEncodedMethod> wave) {
     onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
   }
 
-  private void waveDone() {
+  private void waveDone(Collection<DexEncodedMethod> wave) {
+    if (options.enableFieldAssignmentTracker) {
+      fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
+    }
     delayedOptimizationFeedback.updateVisibleOptimizationInfo();
     onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
     onWaveDoneActions = null;
@@ -1143,6 +1143,8 @@
     // we will return with finalizeEmptyThrowingCode() above.
     assert code.verifyTypes(appView);
 
+    assertionsRewriter.run(method, code, timing);
+
     if (serviceLoaderRewriter != null) {
       assert appView.appInfo().hasLiveness();
       timing.begin("Rewrite service loaders");
@@ -1170,8 +1172,6 @@
       timing.end();
     }
 
-    assertionsRewriter.run(method, code, timing);
-
     previous = printMethod(code, "IR after disable assertions (SSA)", previous);
 
     timing.begin("Insert assume instructions");
@@ -1493,9 +1493,12 @@
         timing.end();
       }
 
-      if (fieldBitAccessAnalysis != null) {
-        timing.begin("Record field access");
-        fieldBitAccessAnalysis.recordFieldAccesses(code, feedback);
+      if (fieldAccessAnalysis != null) {
+        timing.begin("Analyze field accesses");
+        fieldAccessAnalysis.recordFieldAccesses(code, feedback, methodProcessor);
+        if (classInitializerDefaultsResult != null) {
+          fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
+        }
         timing.end();
       }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index b65b164..246d865 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -27,9 +27,9 @@
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 6c42599..acba17d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.IROrdering;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -35,8 +34,7 @@
 
   interface WaveStartAction {
 
-    void notifyWaveStart(Collection<DexEncodedMethod> wave, ExecutorService executorService)
-        throws ExecutionException;
+    void notifyWaveStart(Collection<DexEncodedMethod> wave);
   }
 
   private final CallSiteInformation callSiteInformation;
@@ -141,7 +139,7 @@
   <E extends Exception> void forEachMethod(
       ThrowingFunction<DexEncodedMethod, Timing, E> consumer,
       WaveStartAction waveStartAction,
-      Action waveDone,
+      Consumer<Collection<DexEncodedMethod>> waveDone,
       Timing timing,
       ExecutorService executorService)
       throws ExecutionException {
@@ -150,7 +148,7 @@
     while (!waves.isEmpty()) {
       wave = waves.removeFirst();
       assert wave.size() > 0;
-      waveStartAction.notifyWaveStart(wave, executorService);
+      waveStartAction.notifyWaveStart(wave);
       merger.add(
           ThreadUtils.processItemsWithResults(
               wave,
@@ -160,7 +158,7 @@
                 return time;
               },
               executorService));
-      waveDone.execute();
+      waveDone.accept(wave);
     }
     merger.end();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
index 9fca058..78d5702 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke;
 import com.google.common.collect.ImmutableMap;
 import java.util.IdentityHashMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 52d047e..e35087a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -56,6 +56,7 @@
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
 public class ClassInitializerDefaultsOptimization {
@@ -75,6 +76,12 @@
       return EMPTY;
     }
 
+    public void forEachOptimizedField(BiConsumer<DexEncodedField, DexValue> consumer) {
+      if (fieldsWithStaticValues != null) {
+        fieldsWithStaticValues.forEach(consumer);
+      }
+    }
+
     public boolean hasStaticValue(DexEncodedField field) {
       if (field.isStatic()) {
         return (fieldsWithStaticValues != null && fieldsWithStaticValues.containsKey(field))
@@ -86,14 +93,14 @@
 
   private class WaveDoneAction implements Action {
 
-    private final Map<DexEncodedField, DexValue> fieldsWithStaticValues;
-    private final Set<DexField> noLongerWrittenFields;
+    private final Map<DexEncodedField, DexValue> fieldsWithStaticValues = new IdentityHashMap<>();
+    private final Set<DexField> noLongerWrittenFields = Sets.newIdentityHashSet();
 
     public WaveDoneAction(
         Map<DexEncodedField, DexValue> fieldsWithStaticValues,
         Set<DexField> noLongerWrittenFields) {
-      this.fieldsWithStaticValues = fieldsWithStaticValues;
-      this.noLongerWrittenFields = noLongerWrittenFields;
+      this.fieldsWithStaticValues.putAll(fieldsWithStaticValues);
+      this.noLongerWrittenFields.addAll(noLongerWrittenFields);
     }
 
     public synchronized void join(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index e17e419..ea419cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1469,6 +1469,8 @@
 
     @Override
     public void buildPrelude(IRBuilder builder) {
+      // If the assertion fails, check DexSourceCode#buildArgumentsWithUnusedArgumentStubs.
+      assert builder.getPrototypeChanges().isEmpty();
       // Fill in the Argument instructions in the argument block.
       for (int i = 0; i < outline.argumentTypes.size(); i++) {
         if (outline.argumentTypes.get(i).isBooleanType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 5088d99..b871461 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -19,9 +19,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.ir.analysis.AbstractError;
 import com.android.tools.r8.ir.analysis.TypeChecker;
@@ -160,9 +160,8 @@
       // This achieved by faking that there is already a method with the given signature.
       for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
         RewrittenPrototypeDescription prototypeChanges =
-            new RewrittenPrototypeDescription(
+            RewrittenPrototypeDescription.createForUninstantiatedTypes(
                 virtualMethod.method.proto.returnType.isAlwaysNull(appView),
-                false,
                 getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
         if (!prototypeChanges.isEmpty()) {
           DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
@@ -296,9 +295,8 @@
         || appView.appInfo().keepConstantArguments.contains(encodedMethod.method)) {
       return RewrittenPrototypeDescription.none();
     }
-    return new RewrittenPrototypeDescription(
+    return RewrittenPrototypeDescription.createForUninstantiatedTypes(
         encodedMethod.method.proto.returnType.isAlwaysNull(appView),
-        false,
         getRemovedArgumentsInfo(encodedMethod, strategy));
   }
 
@@ -333,41 +331,10 @@
   private DexMethod getNewMethodSignature(
       DexEncodedMethod encodedMethod, RewrittenPrototypeDescription prototypeChanges) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
-
     DexMethod method = encodedMethod.method;
-    RemovedArgumentsInfo removedArgumentsInfo = prototypeChanges.getRemovedArgumentsInfo();
+    DexProto newProto = prototypeChanges.rewriteProto(encodedMethod, dexItemFactory);
 
-    if (prototypeChanges.isEmpty()) {
-      return method;
-    }
-
-    DexType newReturnType =
-        prototypeChanges.hasBeenChangedToReturnVoid()
-            ? dexItemFactory.voidType
-            : method.proto.returnType;
-
-    DexType[] newParameters;
-    if (removedArgumentsInfo.hasRemovedArguments()) {
-      // Currently not allowed to remove the receiver of an instance method. This would involve
-      // changing invoke-direct/invoke-virtual into invoke-static.
-      assert encodedMethod.isStatic() || !removedArgumentsInfo.isArgumentRemoved(0);
-      newParameters =
-          new DexType
-              [method.proto.parameters.size() - removedArgumentsInfo.numberOfRemovedArguments()];
-      int offset = encodedMethod.isStatic() ? 0 : 1;
-      int newParametersIndex = 0;
-      for (int argumentIndex = 0; argumentIndex < method.proto.parameters.size(); ++argumentIndex) {
-        if (!removedArgumentsInfo.isArgumentRemoved(argumentIndex + offset)) {
-          newParameters[newParametersIndex] = method.proto.parameters.values[argumentIndex];
-          newParametersIndex++;
-        }
-      }
-    } else {
-      newParameters = method.proto.parameters.values;
-    }
-
-    return dexItemFactory.createMethod(
-        method.holder, dexItemFactory.createProto(newReturnType, newParameters), method.name);
+    return dexItemFactory.createMethod(method.holder, newProto, method.name);
   }
 
   public void rewrite(IRCode code) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index aec5219..705b2bd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -16,8 +16,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentsInfo;
 import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -329,25 +330,8 @@
 
   private DexProto createProtoWithRemovedArguments(
       DexEncodedMethod encodedMethod, RemovedArgumentsInfo unused) {
-    DexMethod method = encodedMethod.method;
-
-    int firstArgumentIndex = encodedMethod.isStatic() ? 0 : 1;
-    int numberOfParameters = method.proto.parameters.size() - unused.numberOfRemovedArguments();
-    if (!encodedMethod.isStatic() && unused.isArgumentRemoved(0)) {
-      numberOfParameters++;
-    }
-
-    DexType[] parameters = new DexType[numberOfParameters];
-    if (numberOfParameters > 0) {
-      int newIndex = 0;
-      for (int oldIndex = 0; oldIndex < method.proto.parameters.size(); oldIndex++) {
-        if (!unused.isArgumentRemoved(oldIndex + firstArgumentIndex)) {
-          parameters[newIndex++] = method.proto.parameters.values[oldIndex];
-        }
-      }
-      assert newIndex == parameters.length;
-    }
-    return appView.dexItemFactory().createProto(method.proto.returnType, parameters);
+    DexType[] parameters = unused.rewriteParameters(encodedMethod);
+    return appView.dexItemFactory().createProto(encodedMethod.method.proto.returnType, parameters);
   }
 
   private static class CollectUsedArguments extends ArgumentUse {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index 91e952a..67a7588 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -32,6 +32,7 @@
   public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
     this.appView = appView;
     register(new BooleanMethodOptimizer(appView));
+    register(new StringMethodOptimizer(appView));
 
     if (LogMethodOptimizer.isEnabled(appView)) {
       register(new LogMethodOptimizer(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
new file mode 100644
index 0000000..5e4033b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class StringMethodOptimizer implements LibraryMethodModelCollection {
+
+  private final AppView<? extends AppInfoWithSubtyping> appView;
+  private final DexItemFactory dexItemFactory;
+
+  StringMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+    this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
+  }
+
+  @Override
+  public DexType getType() {
+    return dexItemFactory.stringType;
+  }
+
+  @Override
+  public void optimize(
+      IRCode code,
+      InstructionListIterator instructionIterator,
+      InvokeMethod invoke,
+      DexEncodedMethod singleTarget,
+      Set<Value> affectedValues) {
+    if (singleTarget.method == dexItemFactory.stringMethods.equals) {
+      optimizeEquals(code, instructionIterator, invoke);
+    }
+  }
+
+  private void optimizeEquals(
+      IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+    if (appView.appInfo().hasLiveness()) {
+      DexType context = code.method.method.holder;
+      Value first = invoke.arguments().get(0).getAliasedValue();
+      Value second = invoke.arguments().get(1).getAliasedValue();
+      if (isPrunedClassNameComparison(first, second, context)
+          || isPrunedClassNameComparison(second, first, context)) {
+        instructionIterator.replaceCurrentInstructionWithConstInt(appView, code, 0);
+      }
+    }
+  }
+
+  /**
+   * Returns true if {@param classNameValue} is defined by calling {@link Class#getName()} and
+   * {@param constStringValue} is a constant string that is identical to the name of a class that
+   * has been pruned by the {@link com.android.tools.r8.shaking.Enqueuer}.
+   */
+  private boolean isPrunedClassNameComparison(
+      Value classNameValue, Value constStringValue, DexType context) {
+    if (classNameValue.isPhi() || constStringValue.isPhi()) {
+      return false;
+    }
+
+    Instruction classNameDefinition = classNameValue.definition;
+    if (!classNameDefinition.isInvokeVirtual()) {
+      return false;
+    }
+
+    DexEncodedMethod singleTarget =
+        classNameDefinition.asInvokeVirtual().lookupSingleTarget(appView, context);
+    if (singleTarget == null || singleTarget.method != dexItemFactory.classMethods.getName) {
+      return false;
+    }
+
+    if (!constStringValue.definition.isDexItemBasedConstString()) {
+      return false;
+    }
+
+    DexItemBasedConstString constString = constStringValue.definition.asDexItemBasedConstString();
+    DexReference reference = constString.getItem();
+    return reference.isDexType()
+        && appView.appInfo().withLiveness().wasPruned(reference.asDexType())
+        && !constString.getNameComputationInfo().needsToComputeName();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 8b662b9..00b5315 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.naming.MethodNameMinifier.State;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DisjointSets;
@@ -241,6 +242,7 @@
 
     private final Set<DexCallSite> callSites = new HashSet<>();
     private final Map<DexMethod, Set<InterfaceReservationState>> methodStates = new HashMap<>();
+    private final List<DexMethod> callSiteCollidingMethods = new ArrayList<>();
 
     void addState(DexMethod method, InterfaceReservationState interfaceState) {
       methodStates.computeIfAbsent(method, m -> new HashSet<>()).add(interfaceState);
@@ -248,6 +250,7 @@
 
     void appendMethodGroupState(InterfaceMethodGroupState state) {
       callSites.addAll(state.callSites);
+      callSiteCollidingMethods.addAll(state.callSiteCollidingMethods);
       for (DexMethod key : state.methodStates.keySet()) {
         methodStates.computeIfAbsent(key, k -> new HashSet<>()).addAll(state.methodStates.get(key));
       }
@@ -455,6 +458,37 @@
             groupState.addCallSite(callSite);
             callSiteMethods.add(wrapped);
           }
+          if (callSiteMethods.isEmpty()) {
+            return;
+          }
+          // For intersection types, we have to iterate all the multiple interfaces to look for
+          // methods with the same signature.
+          List<DexType> implementedInterfaces =
+              LambdaDescriptor.getInterfaces(callSite, appView.appInfo());
+          if (implementedInterfaces != null) {
+            for (int i = 1; i < implementedInterfaces.size(); i++) {
+              // Add the merging state for all additional implemented interfaces into the state
+              // for the group, if the name is different, to ensure that we do not pick the same
+              // name.
+              DexClass iface = appView.definitionFor(implementedInterfaces.get(i));
+              assert iface.isInterface();
+              for (DexEncodedMethod implementedMethod : implementedMethods) {
+                for (DexEncodedMethod virtualMethod : iface.virtualMethods()) {
+                  boolean differentName =
+                      !implementedMethod.method.name.equals(virtualMethod.method.name);
+                  if (differentName
+                      && MethodJavaSignatureEquivalence.getEquivalenceIgnoreName()
+                          .equivalent(implementedMethod.method, virtualMethod.method)) {
+                    InterfaceMethodGroupState interfaceMethodGroupState =
+                        globalStateMap.computeIfAbsent(
+                            equivalence.wrap(implementedMethod.method),
+                            k -> new InterfaceMethodGroupState());
+                    interfaceMethodGroupState.callSiteCollidingMethods.add(virtualMethod.method);
+                  }
+                }
+              }
+            }
+          }
           if (callSiteMethods.size() > 1) {
             // Implemented interfaces have different protos. Unify them.
             Wrapper<DexMethod> mainKey = callSiteMethods.iterator().next();
@@ -545,6 +579,27 @@
         callSiteRenamings.put(callSite, newName);
       }
     }
+
+    // After all naming is completed for callsites, we must ensure to rename all interface methods
+    // that can collide with the callsite method name.
+    for (Wrapper<DexMethod> interfaceMethodGroup : nonReservedMethodGroups) {
+      InterfaceMethodGroupState groupState = globalStateMap.get(interfaceMethodGroup);
+      if (groupState.callSiteCollidingMethods.isEmpty()) {
+        continue;
+      }
+      DexMethod key = interfaceMethodGroup.get();
+      MethodNamingState<?> keyNamingState = minifierState.getNamingState(key.holder);
+      DexString existingRenaming = keyNamingState.newOrReservedNameFor(key);
+      assert existingRenaming != null;
+      for (DexMethod collidingMethod : groupState.callSiteCollidingMethods) {
+        DexString newNameInGroup = newNameInGroup(collidingMethod, keyNamingState, groupState);
+        minifierState.putRenaming(collidingMethod, newNameInGroup);
+        MethodNamingState<?> methodNamingState =
+            minifierState.getNamingState(collidingMethod.holder);
+        methodNamingState.addRenaming(newNameInGroup, collidingMethod);
+        keyNamingState.addRenaming(newNameInGroup, collidingMethod);
+      }
+    }
     timing.end();
 
     timing.end(); // end compute timing
@@ -563,6 +618,12 @@
     return newName;
   }
 
+  private DexString newNameInGroup(
+      DexMethod method, MethodNamingState<?> namingState, InterfaceMethodGroupState groupState) {
+    // Check if the name is available in all states.
+    return namingState.nextName(method, (candidate, ignore) -> groupState.isAvailable(candidate));
+  }
+
   private void patchUpChildrenInReservationStates() {
     for (Map.Entry<DexType, InterfaceReservationState> entry : interfaceStateMap.entrySet()) {
       for (DexType parent : entry.getValue().iface.interfaces.values) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index 7926de0..85c2f0f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -62,8 +62,12 @@
         return candidate;
       }
     }
+    return nextName(method, isAvailable);
+  }
+
+  DexString nextName(DexMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
     InternalNewNameState internalState = getOrCreateInternalState(method);
-    newName = namingStrategy.next(method, internalState, isAvailable);
+    DexString newName = namingStrategy.next(method, internalState, isAvailable);
     assert newName != null;
     return newName;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index b90d37e..cd9db29 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -113,7 +113,6 @@
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 import org.objectweb.asm.Opcodes;
 
 /**
@@ -411,34 +410,6 @@
     }
   }
 
-  private Set<DexField> getNonPinnedWrittenFields(Predicate<DexEncodedField> predicate) {
-    Set<DexField> result = Sets.newIdentityHashSet();
-    fieldAccessInfoCollection.forEach(
-        info -> {
-          if (info == MISSING_FIELD_ACCESS_INFO) {
-            return;
-          }
-          // Note that it is safe to use definitionFor() here, and not lookupField(), since the
-          // field held by `info` is a direct reference to the definition of the field.
-          DexEncodedField encodedField = appView.definitionFor(info.getField());
-          if (encodedField == null) {
-            assert false;
-            return;
-          }
-          if (encodedField.isProgramField(appInfo)
-              && info.isWritten()
-              && predicate.test(encodedField)) {
-            result.add(encodedField.field);
-          }
-        });
-    result.removeAll(
-        pinnedItems.stream()
-            .filter(DexReference::isDexField)
-            .map(DexReference::asDexField)
-            .collect(Collectors.toSet()));
-    return result;
-  }
-
   private static <T> SetWithReason<T> newSetWithoutReasonReporter() {
     return new SetWithReason<>((f, r) -> {});
   }
@@ -570,20 +541,29 @@
   }
 
   public boolean registerFieldRead(DexField field, DexEncodedMethod context) {
-    return registerFieldAccess(field, context, true);
+    return registerFieldAccess(field, context, true, false);
+  }
+
+  public boolean registerReflectiveFieldRead(DexField field, DexEncodedMethod context) {
+    return registerFieldAccess(field, context, true, true);
   }
 
   public boolean registerFieldWrite(DexField field, DexEncodedMethod context) {
-    return registerFieldAccess(field, context, false);
+    return registerFieldAccess(field, context, false, false);
   }
 
-  public boolean registerFieldAccess(DexField field, DexEncodedMethod context) {
-    boolean changed = registerFieldAccess(field, context, true);
-    changed |= registerFieldAccess(field, context, false);
+  public boolean registerReflectiveFieldWrite(DexField field, DexEncodedMethod context) {
+    return registerFieldAccess(field, context, false, true);
+  }
+
+  public boolean registerReflectiveFieldAccess(DexField field, DexEncodedMethod context) {
+    boolean changed = registerFieldAccess(field, context, true, true);
+    changed |= registerFieldAccess(field, context, false, true);
     return changed;
   }
 
-  private boolean registerFieldAccess(DexField field, DexEncodedMethod context, boolean isRead) {
+  private boolean registerFieldAccess(
+      DexField field, DexEncodedMethod context, boolean isRead, boolean isReflective) {
     FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field);
     if (info == null) {
       DexEncodedField encodedField = appInfo.resolveField(field);
@@ -612,6 +592,9 @@
     } else if (info == MISSING_FIELD_ACCESS_INFO) {
       return false;
     }
+    if (isReflective) {
+      info.setHasReflectiveAccess();
+    }
     return isRead ? info.recordRead(field, context) : info.recordWrite(field, context);
   }
 
@@ -1740,8 +1723,8 @@
     }
     assert clazz.accessFlags.isInterface();
     transitionReachableVirtualMethods(clazz, seen.newNestedScope());
-    for (DexType subInterface : clazz.interfaces.values) {
-      transitionDefaultMethodsForInstantiatedClass(subInterface, seen);
+    for (DexType superInterface : clazz.interfaces.values) {
+      transitionDefaultMethodsForInstantiatedClass(superInterface, seen);
     }
   }
 
@@ -2100,6 +2083,7 @@
       return;
     }
 
+    // TODO(mkroghj): Remove pinnedItems check here.
     if (instantiatedTypes.contains(clazz)
         || instantiatedInterfaceTypes.contains(clazz)
         || pinnedItems.contains(clazz.type)) {
@@ -2118,8 +2102,8 @@
         if (currentClass == null || currentClass.lookupVirtualMethod(possibleTarget) != null) {
           continue;
         }
-        // TODO(zerny): Why does not not confer with lambdas and pinned too?
-        if (instantiatedTypes.contains(currentClass)) {
+        if (instantiatedTypes.contains(currentClass)
+            || instantiatedInterfaceTypes.contains(currentClass)) {
           markVirtualMethodAsLive(
               clazz,
               encodedPossibleTarget,
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index f27bd26..1cfd120 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1281,9 +1281,6 @@
             String message =
                 "Proguard configuration rule does not match anything: `" + rule.toString() + "`";
             StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
-            if (!options.testing.allowUnusedProguardConfigurationRules) {
-              throw options.reporter.fatalError(diagnostic);
-            }
             if (options.testing.reportUnusedProguardConfigurationRules) {
               options.reporter.info(diagnostic);
             }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6ab0836..36a7030 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -897,10 +898,10 @@
 
       for (DexEncodedMethod directMethod : source.directMethods()) {
         if (directMethod.isInstanceInitializer()) {
-          add(
-              directMethods,
-              renameConstructor(directMethod, availableMethodSignatures),
-              MethodSignatureEquivalence.get());
+          DexEncodedMethod resultingConstructor =
+              renameConstructor(directMethod, availableMethodSignatures);
+          add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
+          blockRedirectionOfSuperCalls(resultingConstructor.method);
         } else {
           DexEncodedMethod resultingDirectMethod =
               renameMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index c565c4e..cccaa07 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -896,9 +896,12 @@
       return this;
     }
 
-    /**
-     * Add Java-bytecode program data.
-     */
+    /** Add Java-bytecode program data. */
+    public Builder addClassProgramData(byte[]... data) {
+      return addClassProgramData(Arrays.asList(data));
+    }
+
+    /** Add Java-bytecode program data. */
     public Builder addClassProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
         addClassProgramData(datum, Origin.unknown());
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 9eb8b6c..d0a4fb1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -192,6 +192,7 @@
 
   // Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
   public boolean enableDynamicTypeOptimization = true;
+  public boolean enableFieldAssignmentTracker = true;
   public boolean enableFieldBitAccessAnalysis =
       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
   public boolean enableHorizontalClassMerging = true;
@@ -1005,7 +1006,6 @@
     public boolean allowInvokeErrors = false;
     public boolean disableL8AnnotationRemoval = false;
     public boolean allowClassInlinerGracefulExit = true;
-    public boolean allowUnusedProguardConfigurationRules = true;
     public boolean reportUnusedProguardConfigurationRules = false;
     public boolean alwaysUsePessimisticRegisterAllocation = false;
     public boolean enableCheckCastAndInstanceOfRemoval = true;
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 117ca7b..80b23ac 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -24,6 +24,16 @@
   }
 
   @Override
+  public String getStdout() {
+    return state.getStdout();
+  }
+
+  @Override
+  public String getStderr() {
+    return state.getStderr();
+  }
+
+  @Override
   public D8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
     return new D8TestRunResult(app, runtime, result);
   }
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index 3cf9b73..e362f78 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.AndroidApp;
 
 public class DXTestCompileResult extends TestCompileResult<DXTestCompileResult, DXTestRunResult> {
@@ -23,6 +24,16 @@
   }
 
   @Override
+  public String getStdout() {
+    throw new Unimplemented("Unexpected attempt to access stdout from dx");
+  }
+
+  @Override
+  public String getStderr() {
+    throw new Unimplemented("Unexpected attempt to access stderr from dx");
+  }
+
+  @Override
   public DXTestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
     return new DXTestRunResult(app, runtime, result);
   }
diff --git a/src/test/java/com/android/tools/r8/DumpInputsTest.java b/src/test/java/com/android/tools/r8/DumpInputsTest.java
index e7f2ef8..84febed 100644
--- a/src/test/java/com/android/tools/r8/DumpInputsTest.java
+++ b/src/test/java/com/android/tools/r8/DumpInputsTest.java
@@ -3,12 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -24,8 +24,6 @@
 @RunWith(Parameterized.class)
 public class DumpInputsTest extends TestBase {
 
-  static final String EXPECTED = StringUtils.lines("Hello, world");
-
   private final TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0}")
@@ -62,6 +60,9 @@
         .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(options -> options.dumpInputToDirectory = dumpDir.toString())
+        .allowDiagnosticInfoMessages()
+        .compile()
+        .assertAllInfoMessagesMatch(containsString("Dumped compilation inputs to:"))
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("Hello, world");
     assertTrue(Files.isDirectory(dumpDir));
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index 04a37fa..75cd5526 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.io.IOException;
@@ -58,6 +59,16 @@
   }
 
   @Override
+  public String getStdout() {
+    throw new Unimplemented("Unexpected attempt to access stdout from external R8");
+  }
+
+  @Override
+  public String getStderr() {
+    throw new Unimplemented("Unexpected attempt to access stderr from external R8");
+  }
+
+  @Override
   public CodeInspector inspector() throws IOException, ExecutionException {
     return new CodeInspector(app, proguardMap);
   }
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index fbb8282..dbb3c6e 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.io.IOException;
@@ -37,6 +38,16 @@
   }
 
   @Override
+  public String getStdout() {
+    throw new Unimplemented("Unexpected attempt to access stdout from Proguard");
+  }
+
+  @Override
+  public String getStderr() {
+    throw new Unimplemented("Unexpected attempt to access stderr from Proguard");
+  }
+
+  @Override
   public CodeInspector inspector() throws IOException, ExecutionException {
     return new CodeInspector(app, proguardMap);
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1dca05c..d80f7cd 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -3,9 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.R8Command.Builder;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
@@ -26,14 +29,24 @@
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import org.hamcrest.core.IsAnything;
 
 public abstract class R8TestBuilder<T extends R8TestBuilder<T>>
     extends TestShrinkerBuilder<R8Command, Builder, R8TestCompileResult, R8TestRunResult, T> {
 
+  enum AllowedDiagnosticMessages {
+    ALL,
+    INFO,
+    NONE,
+    WARNING
+  }
+
   R8TestBuilder(TestState state, Builder builder, Backend backend) {
     super(state, builder, backend);
   }
 
+  private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
+  private boolean allowUnusedProguardConfigurationRules = false;
   private boolean enableInliningAnnotations = false;
   private boolean enableNeverClassInliningAnnotations = false;
   private boolean enableMergeAnnotations = false;
@@ -105,14 +118,47 @@
         builder.build(),
         optionsConsumer.andThen(
             options -> box.proguardConfiguration = options.getProguardConfiguration()));
-    return new R8TestCompileResult(
-        getState(),
-        getOutputMode(),
-        app.get(),
-        box.proguardConfiguration,
-        box.syntheticProguardRules,
-        proguardMapBuilder.toString(),
-        graphConsumer);
+    R8TestCompileResult compileResult =
+        new R8TestCompileResult(
+            getState(),
+            getOutputMode(),
+            app.get(),
+            box.proguardConfiguration,
+            box.syntheticProguardRules,
+            proguardMapBuilder.toString(),
+            graphConsumer);
+    switch (allowedDiagnosticMessages) {
+      case ALL:
+        compileResult.assertDiagnosticMessageThatMatches(new IsAnything<>());
+        break;
+      case INFO:
+        compileResult.assertOnlyInfos();
+        break;
+      case NONE:
+        if (allowUnusedProguardConfigurationRules) {
+          compileResult
+              .assertAllInfoMessagesMatch(
+                  containsString("Proguard configuration rule does not match anything"))
+              .assertNoErrorMessages()
+              .assertNoWarningMessages();
+        } else {
+          compileResult.assertNoMessages();
+        }
+        break;
+      case WARNING:
+        compileResult.assertOnlyWarnings();
+        break;
+      default:
+        throw new Unreachable();
+    }
+    if (allowUnusedProguardConfigurationRules) {
+      compileResult.assertInfoMessageThatMatches(
+          containsString("Proguard configuration rule does not match anything"));
+    } else {
+      compileResult.assertNoInfoMessageThatMatches(
+          containsString("Proguard configuration rule does not match anything"));
+    }
+    return compileResult;
   }
 
   public Builder getBuilder() {
@@ -201,9 +247,45 @@
     return addOptionsModification(options -> options.testing.allowClassInlinerGracefulExit = true);
   }
 
+  public T allowDiagnosticMessages() {
+    assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+    allowedDiagnosticMessages = AllowedDiagnosticMessages.ALL;
+    return self();
+  }
+
+  public T allowDiagnosticInfoMessages() {
+    return allowDiagnosticInfoMessages(true);
+  }
+
+  public T allowDiagnosticInfoMessages(boolean condition) {
+    if (condition) {
+      assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+      allowedDiagnosticMessages = AllowedDiagnosticMessages.INFO;
+    }
+    return self();
+  }
+
+  public T allowDiagnosticWarningMessages() {
+    return allowDiagnosticWarningMessages(true);
+  }
+
+  public T allowDiagnosticWarningMessages(boolean condition) {
+    if (condition) {
+      assert allowedDiagnosticMessages == AllowedDiagnosticMessages.NONE;
+      allowedDiagnosticMessages = AllowedDiagnosticMessages.WARNING;
+    }
+    return self();
+  }
+
   public T allowUnusedProguardConfigurationRules() {
-    return addOptionsModification(
-        options -> options.testing.allowUnusedProguardConfigurationRules = true);
+    return allowUnusedProguardConfigurationRules(true);
+  }
+
+  public T allowUnusedProguardConfigurationRules(boolean condition) {
+    if (condition) {
+      allowUnusedProguardConfigurationRules = true;
+    }
+    return self();
   }
 
   public T enableAlwaysInliningAnnotations() {
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index c4ec2aa..ae6fc69 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -53,6 +53,16 @@
   }
 
   @Override
+  public String getStdout() {
+    return state.getStdout();
+  }
+
+  @Override
+  public String getStderr() {
+    return state.getStderr();
+  }
+
+  @Override
   public CodeInspector inspector() throws IOException, ExecutionException {
     return new CodeInspector(app, proguardMap);
   }
diff --git a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
index 8d6b8d2..06cba56 100644
--- a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
+++ b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
@@ -4,7 +4,9 @@
 
 package com.android.tools.r8;
 
+import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
@@ -57,8 +59,16 @@
             : containsString("AbstractMethodError");
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class)
+        .allowDiagnosticWarningMessages(
+            parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
+        .compile()
+        .assertAllWarningMessagesMatch(
+            anyOf(
+                equalTo(
+                    "Lambda expression implements missing interface `java.util.function.Supplier`"),
+                containsString("required for default or static interface methods desugaring")))
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatMatches(expectedError);
   }
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index e04a2c1..87cc650 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -5,7 +5,10 @@
 
 import static com.android.tools.r8.TestBase.Backend.DEX;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.TestBase.Backend;
@@ -78,6 +81,15 @@
 
   public abstract TestDiagnosticMessages getDiagnosticMessages();
 
+  public CR inspectDiagnosticMessages(Consumer<TestDiagnosticMessages> consumer) {
+    consumer.accept(getDiagnosticMessages());
+    return self();
+  }
+
+  public abstract String getStdout();
+
+  public abstract String getStderr();
+
   public OutputMode getOutputMode() {
     return outputMode;
   }
@@ -268,21 +280,55 @@
     return self();
   }
 
+  public CR assertDiagnosticMessageThatMatches(Matcher<String> matcher) {
+    getDiagnosticMessages().assertDiagnosticMessageThatMatches(matcher);
+    return self();
+  }
+
   public CR assertInfoMessageThatMatches(Matcher<String> matcher) {
     getDiagnosticMessages().assertInfoMessageThatMatches(matcher);
     return self();
   }
 
+  public CR assertAllInfoMessagesMatch(Matcher<String> matcher) {
+    return assertNoInfoMessageThatMatches(not(matcher));
+  }
+
   public CR assertNoInfoMessageThatMatches(Matcher<String> matcher) {
     getDiagnosticMessages().assertNoInfoMessageThatMatches(matcher);
     return self();
   }
 
+  public CR assertAtLeastOneInfoMessage() {
+    assertTrue(getDiagnosticMessages().getInfos().size() >= 1);
+    return self();
+  }
+
+  public CR assertInfosCount(int count) {
+    getDiagnosticMessages().assertInfosCount(count);
+    return self();
+  }
+
+  public CR assertNoInfoMessages() {
+    getDiagnosticMessages().assertInfosCount(0);
+    return self();
+  }
+
   public CR assertWarningMessageThatMatches(Matcher<String> matcher) {
     getDiagnosticMessages().assertWarningMessageThatMatches(matcher);
     return self();
   }
 
+  public CR assertAllWarningMessagesMatch(Matcher<String> matcher) {
+    getDiagnosticMessages().assertNoWarningMessageThatMatches(not(matcher));
+    return self();
+  }
+
+  public CR assertNoWarningMessages() {
+    getDiagnosticMessages().assertWarningsCount(0);
+    return self();
+  }
+
   public CR assertNoWarningMessageThatMatches(Matcher<String> matcher) {
     getDiagnosticMessages().assertNoWarningMessageThatMatches(matcher);
     return self();
@@ -293,11 +339,36 @@
     return self();
   }
 
+  public CR assertNoErrorMessages() {
+    getDiagnosticMessages().assertErrorsCount(0);
+    return self();
+  }
+
   public CR assertNoErrorMessageThatMatches(Matcher<String> matcher) {
     getDiagnosticMessages().assertNoErrorMessageThatMatches(matcher);
     return self();
   }
 
+  public CR assertNoStdout() {
+    assertEquals("", getStdout());
+    return self();
+  }
+
+  public CR assertStdoutThatMatches(Matcher<String> matcher) {
+    assertThat(getStdout(), matcher);
+    return self();
+  }
+
+  public CR assertNoStderr() {
+    assertEquals("", getStderr());
+    return self();
+  }
+
+  public CR assertStderrThatMatches(Matcher<String> matcher) {
+    assertThat(getStderr(), matcher);
+    return self();
+  }
+
   public CR disassemble(PrintStream ps) throws IOException, ExecutionException {
     ToolHelper.disassemble(app, ps);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 8c6e7c2..628e9c5 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,8 +10,10 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.ForwardingOutputStream;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.base.Suppliers;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.file.Path;
@@ -35,20 +37,22 @@
   public static final Consumer<InternalOptions> DEFAULT_OPTIONS =
       options -> {
         options.testing.allowClassInlinerGracefulExit = false;
-        options.testing.allowUnusedProguardConfigurationRules = false;
         options.testing.reportUnusedProguardConfigurationRules = true;
       };
 
   final Backend backend;
 
   // Default initialized setup. Can be overwritten if needed.
+  private boolean allowStdoutMessages = false;
+  private boolean allowStderrMessages = false;
   private boolean useDefaultRuntimeLibrary = true;
   private final List<Path> additionalRunClassPath = new ArrayList<>();
   private ProgramConsumer programConsumer;
   private StringConsumer mainDexListConsumer;
   private AndroidApiLevel defaultMinApiLevel = ToolHelper.getMinApiLevelForDexVm();
   private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
-  private PrintStream stdout = null;
+  private ByteArrayOutputStream stdout = null;
+  private ByteArrayOutputStream stderr = null;
   protected OutputMode outputMode = OutputMode.DexIndexed;
 
   TestCompilerBuilder(TestState state, B builder, Backend backend) {
@@ -92,16 +96,32 @@
       }
     }
     PrintStream oldOut = System.out;
+    PrintStream oldErr = System.err;
+    CR cr = null;
     try {
       if (stdout != null) {
-        System.setOut(stdout);
+        System.setOut(new PrintStream(new ForwardingOutputStream(stdout, System.out)));
       }
-      CR cr = internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build));
+      if (stderr != null) {
+        System.setErr(new PrintStream(new ForwardingOutputStream(stderr, System.err)));
+      }
+      cr = internalCompile(builder, optionsConsumer, Suppliers.memoize(sink::build));
       cr.addRunClasspathFiles(additionalRunClassPath);
       return cr;
     } finally {
       if (stdout != null) {
+        getState().setStdout(stdout.toString());
         System.setOut(oldOut);
+        if (cr != null && !allowStdoutMessages) {
+          cr.assertNoStdout();
+        }
+      }
+      if (stderr != null) {
+        getState().setStderr(stderr.toString());
+        System.setErr(oldErr);
+        if (cr != null && !allowStderrMessages) {
+          cr.assertNoStderr();
+        }
       }
     }
   }
@@ -288,12 +308,28 @@
     return self();
   }
 
-  public T redirectStdOut(PrintStream printStream) {
-    assert stdout == null;
-    stdout = printStream;
+  public T allowStdoutMessages() {
+    allowStdoutMessages = true;
     return self();
   }
 
+  public T collectStdout() {
+    assert stdout == null;
+    stdout = new ByteArrayOutputStream();
+    return allowStdoutMessages();
+  }
+
+  public T allowStderrMessages() {
+    allowStdoutMessages = true;
+    return self();
+  }
+
+  public T collectStderr() {
+    assert stderr == null;
+    stderr = new ByteArrayOutputStream();
+    return allowStderrMessages();
+  }
+
   public T enableCoreLibraryDesugaring(AndroidApiLevel minAPILevel) {
     return enableCoreLibraryDesugaring(minAPILevel, null);
   }
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 702815a..0dd7e67 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -9,35 +9,41 @@
 
 public interface TestDiagnosticMessages {
 
-  public List<Diagnostic> getInfos();
+  List<Diagnostic> getInfos();
 
-  public List<Diagnostic> getWarnings();
+  List<Diagnostic> getWarnings();
 
-  public List<Diagnostic> getErrors();
+  List<Diagnostic> getErrors();
 
-  public TestDiagnosticMessages assertNoMessages();
+  TestDiagnosticMessages assertNoMessages();
 
-  public TestDiagnosticMessages assertOnlyInfos();
+  TestDiagnosticMessages assertOnlyInfos();
 
-  public TestDiagnosticMessages assertOnlyWarnings();
+  TestDiagnosticMessages assertOnlyWarnings();
 
-  public TestDiagnosticMessages assertOnlyErrors();
+  TestDiagnosticMessages assertOnlyErrors();
 
-  public TestDiagnosticMessages assertInfosCount(int count);
+  TestDiagnosticMessages assertInfosCount(int count);
 
-  public TestDiagnosticMessages assertWarningsCount(int count);
+  TestDiagnosticMessages assertWarningsCount(int count);
 
-  public TestDiagnosticMessages assertErrorsCount(int count);
+  TestDiagnosticMessages assertErrorsCount(int count);
 
-  public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertDiagnosticMessageThatMatches(Matcher<String> matcher);
 
-  public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher);
 
-  public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertAllInfoMessagesMatch(Matcher<String> matcher);
 
-  public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher);
 
-  public TestDiagnosticMessages assertErrorMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
 
-  public TestDiagnosticMessages assertNoErrorMessageThatMatches(Matcher<String> matcher);
+  TestDiagnosticMessages assertAllWarningMessagesMatch(Matcher<String> matcher);
+
+  TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
+
+  TestDiagnosticMessages assertErrorMessageThatMatches(Matcher<String> matcher);
+
+  TestDiagnosticMessages assertNoErrorMessageThatMatches(Matcher<String> matcher);
 }
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 478a60d..44f046b 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8;
 
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.List;
 import org.hamcrest.Matcher;
@@ -122,22 +124,24 @@
   }
 
   private TestDiagnosticMessages assertMessageThatMatches(
-      List<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
-    assertNotEquals(0, diagnostics.size());
-    for (int i = 0; i < diagnostics.size(); i++) {
-      if (matcher.matches(diagnostics.get(i).getDiagnosticMessage())) {
+      Iterable<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
+    int numberOfDiagnostics = 0;
+    for (Diagnostic diagnostic : diagnostics) {
+      if (matcher.matches(diagnostic.getDiagnosticMessage())) {
         return this;
       }
+      numberOfDiagnostics++;
     }
+    assertNotEquals(0, numberOfDiagnostics);
     StringBuilder builder = new StringBuilder("No " + tag + " matches " + matcher.toString());
     builder.append(System.lineSeparator());
     if (getWarnings().size() == 0) {
       builder.append("There were no " + tag + "s.");
     } else {
-      builder.append("There were " + diagnostics.size() + " "+ tag + "s:");
+      builder.append("There were " + numberOfDiagnostics + " " + tag + "s:");
       builder.append(System.lineSeparator());
-      for (int i = 0; i < diagnostics.size(); i++) {
-        builder.append(diagnostics.get(i).getDiagnosticMessage());
+      for (Diagnostic diagnostic : diagnostics) {
+        builder.append(diagnostic.getDiagnosticMessage());
         builder.append(System.lineSeparator());
       }
     }
@@ -156,18 +160,38 @@
     return this;
   }
 
+  @Override
+  public TestDiagnosticMessages assertDiagnosticMessageThatMatches(Matcher<String> matcher) {
+    return assertMessageThatMatches(
+        Iterables.concat(getInfos(), getWarnings(), getErrors()), "diagnostic message", matcher);
+  }
+
+  @Override
   public TestDiagnosticMessages assertInfoMessageThatMatches(Matcher<String> matcher) {
     return assertMessageThatMatches(getInfos(), "info", matcher);
   }
 
+  @Override
+  public TestDiagnosticMessages assertAllInfoMessagesMatch(Matcher<String> matcher) {
+    return assertNoInfoMessageThatMatches(not(matcher));
+  }
+
+  @Override
   public TestDiagnosticMessages assertNoInfoMessageThatMatches(Matcher<String> matcher) {
     return assertNoMessageThatMatches(getInfos(), "info", matcher);
   }
 
+  @Override
   public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher) {
     return assertMessageThatMatches(getWarnings(), "warning", matcher);
   }
 
+  @Override
+  public TestDiagnosticMessages assertAllWarningMessagesMatch(Matcher<String> matcher) {
+    return assertNoWarningMessageThatMatches(not(matcher));
+  }
+
+  @Override
   public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher) {
     return assertNoMessageThatMatches(getWarnings(), "warning", matcher);
   }
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 35d37cc..b29d851 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -160,6 +160,10 @@
     return withApiFilter(api -> true);
   }
 
+  public TestParametersBuilder withApiLevel(AndroidApiLevel api) {
+    return withApiFilter(api::equals);
+  }
+
   public TestParametersBuilder withApiLevelsStartingAtIncluding(AndroidApiLevel startInclusive) {
     return withApiFilter(api -> startInclusive.getLevel() <= api.getLevel());
   }
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
index bdb4e2f..78388bc 100644
--- a/src/test/java/com/android/tools/r8/TestState.java
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -12,6 +12,9 @@
   private final TemporaryFolder temp;
   private final TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl();
 
+  private String stdout;
+  private String stderr;
+
   public TestState(TemporaryFolder temp) {
     this.temp = temp;
   }
@@ -27,4 +30,20 @@
   public TestDiagnosticMessages getDiagnosticsMessages() {
     return messages;
   }
+
+  public String getStdout() {
+    return stdout;
+  }
+
+  void setStdout(String stdout) {
+    this.stdout = stdout;
+  }
+
+  public String getStderr() {
+    return stderr;
+  }
+
+  void setStderr(String stderr) {
+    this.stderr = stderr;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index ddb0608..1bde24a 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.google.common.io.ByteStreams.toByteArray;
+import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -103,7 +104,14 @@
           .setMode(mode)
           .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
           .addKeepRuleFiles(MAIN_KEEP)
+          .allowDiagnosticInfoMessages(mode == CompilationMode.DEBUG)
           .compile()
+          .assertAllInfoMessagesMatch(
+              anyOf(
+                  containsString("Stripped invalid locals information from 1 method."),
+                  containsString("Methods with invalid locals information:"),
+                  containsString(
+                      "Some warnings are typically a sign of using an outdated Java toolchain.")))
           .apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
           .writeToZip(jar);
     }
diff --git a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
index 5cbb40d..363008d 100644
--- a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
@@ -180,13 +180,12 @@
         .addProgramClassFileData(junitClasses)
         .addKeepAllClassesRule()
         .addOptionsModification(options -> options.lookupLibraryBeforeProgram = false)
-        .compileWithExpectedDiagnostics(
+        .allowDiagnosticWarningMessages(libraryContainsJUnit())
+        .compile()
+        .inspectDiagnosticMessages(
             diagnostics -> {
               if (libraryContainsJUnit()) {
-                diagnostics.assertOnlyWarnings();
                 checkDiagnostics(diagnostics.getWarnings());
-              } else {
-                diagnostics.assertNoMessages();
               }
             })
         .inspect(this::testCaseClassInResult);
diff --git a/src/test/java/com/android/tools/r8/classmerging/B141942381.java b/src/test/java/com/android/tools/r8/classmerging/B141942381.java
index 2c47534..36f320b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/B141942381.java
+++ b/src/test/java/com/android/tools/r8/classmerging/B141942381.java
@@ -28,7 +28,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public B141942381(TestParameters parameters) {
@@ -49,7 +49,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(B141942381.class)
         .addKeepMainRule(TestClass.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .addKeepAttributes("Signatures")
         .enableNeverClassInliningAnnotations()
         .noMinification()
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
index 09489c9..6ccae1f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.StringConsumer.FileConsumer;
 import com.android.tools.r8.TestBase;
@@ -140,14 +141,17 @@
             "classmerging.ArrayTypeCollisionTest$A",
             "classmerging.ArrayTypeCollisionTest$B");
     runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                getProguardConfig(
+                    EXAMPLE_KEEP,
+                    "-neverinline public class classmerging.ArrayTypeCollisionTest {",
+                    "  static void method(...);",
+                    "}"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         programFiles,
-        preservedClassNames::contains,
-        getProguardConfig(
-            EXAMPLE_KEEP,
-            "-neverinline public class classmerging.ArrayTypeCollisionTest {",
-            "  static void method(...);",
-            "}"));
+        preservedClassNames::contains);
   }
 
   /**
@@ -199,17 +203,18 @@
 
     // Run test.
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                "-keep class " + main + " {",
+                "  public static void main(...);",
+                "}",
+                "-neverinline class " + main + " {",
+                "  static classmerging.A[] method(...);",
+                "  static classmerging.B[] method(...);",
+                "}"),
         main,
         jasminBuilder.build(),
-        preservedClassNames::contains,
-        StringUtils.joinLines(
-            "-keep class " + main + " {",
-            "  public static void main(...);",
-            "}",
-            "-neverinline class " + main + " {",
-            "  static classmerging.A[] method(...);",
-            "  static classmerging.B[] method(...);",
-            "}"));
+        preservedClassNames::contains);
   }
 
   // This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
@@ -231,7 +236,13 @@
     Set<String> preservedClassNames =
         ImmutableSet.of("classmerging.CallGraphCycleTest", "classmerging.CallGraphCycleTest$B");
     for (int i = 0; i < 5; i++) {
-      runTest(main, programFiles, preservedClassNames::contains);
+      runTest(
+          testForR8(Backend.DEX)
+              .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+              .allowUnusedProguardConfigurationRules(),
+          main,
+          programFiles,
+          preservedClassNames::contains);
     }
   }
 
@@ -250,10 +261,12 @@
             "classmerging.ConflictInGeneratedNameTest$B");
     CodeInspector inspector =
         runTestOnInput(
+            testForR8(Backend.DEX)
+                .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+                .allowUnusedProguardConfigurationRules(),
             main,
             readProgramFiles(programFiles),
             preservedClassNames::contains,
-            getProguardConfig(EXAMPLE_KEEP),
             options -> {
               configure(options);
               // Avoid that direct methods in B get inlined.
@@ -321,7 +334,13 @@
         ImmutableSet.of(
             "classmerging.FieldCollisionTest",
             "classmerging.FieldCollisionTest$B");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -342,10 +361,12 @@
             "classmerging.LambdaRewritingTest$FunctionImpl",
             "classmerging.LambdaRewritingTest$InterfaceImpl");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         name -> preservedClassNames.contains(name) || name.contains("$Lambda$"),
-        getProguardConfig(JAVA8_EXAMPLE_KEEP),
         options -> {
           this.configure(options);
           options.enableClassInlining = false;
@@ -367,10 +388,12 @@
             "classmerging.ConflictingInterfaceSignaturesTest",
             "classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         preservedClassNames::contains,
-        getProguardConfig(EXAMPLE_KEEP),
         options -> {
           this.configure(options);
           options.enableInlining = false;
@@ -399,7 +422,13 @@
             "classmerging.MethodCollisionTest$B",
             "classmerging.MethodCollisionTest$C",
             "classmerging.MethodCollisionTest$D");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -418,7 +447,12 @@
             "classmerging.NestedDefaultInterfaceMethodsTest$B",
             "classmerging.NestedDefaultInterfaceMethodsTest$C");
     runTest(
-        main, programFiles, preservedClassNames::contains, getProguardConfig(JAVA8_EXAMPLE_KEEP));
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -436,14 +470,16 @@
             "classmerging.NestedDefaultInterfaceMethodsTest$B",
             "classmerging.NestedDefaultInterfaceMethodsTest$C");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         AndroidApp.builder()
             .addProgramFiles(programFiles)
             .addClassProgramData(
                 NestedDefaultInterfaceMethodsTestDump.CDump.dump(), Origin.unknown())
             .build(),
-        preservedClassNames::contains,
-        getProguardConfig(JAVA8_EXAMPLE_KEEP));
+        preservedClassNames::contains);
   }
 
   @Test
@@ -463,10 +499,12 @@
             "classmerging.PinnedParameterTypesTest$InterfaceImpl",
             "classmerging.PinnedParameterTypesTest$TestClass");
     runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         programFiles,
-        preservedClassNames::contains,
-        getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"));
+        preservedClassNames::contains);
   }
 
   @Test
@@ -486,10 +524,12 @@
             "classmerging.PinnedArrayParameterTypesTest$InterfaceImpl",
             "classmerging.PinnedArrayParameterTypesTest$TestClass");
     runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         programFiles,
-        preservedClassNames::contains,
-        getProguardConfig(EXAMPLE_KEEP, "-keepparameternames"));
+        preservedClassNames::contains);
   }
 
   @Test
@@ -515,10 +555,12 @@
                 "    1:1:void <init>():20:20 -> <init>",
                 "    1:1:void test():23:23 -> test");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         Predicates.alwaysTrue(),
-        getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
           options.enableVerticalClassMerging = false;
@@ -546,10 +588,12 @@
     Set<String> preservedClassNames =
         ImmutableSet.of("classmerging.ProguardFieldMapTest", "classmerging.ProguardFieldMapTest$B");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         preservedClassNames::contains,
-        getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -583,10 +627,12 @@
                 "    1:1:void <init>():22:22 -> <init>",
                 "    1:2:void method():26:27 -> method");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         Predicates.alwaysTrue(),
-        getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
           options.enableVerticalClassMerging = false;
@@ -617,10 +663,12 @@
         ImmutableSet.of(
             "classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         preservedClassNames::contains,
-        getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -655,12 +703,16 @@
                 "    2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
                 "    2:2:void method():27 -> method");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                getProguardConfig(
+                    EXAMPLE_KEEP,
+                    "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+                        + " method(); }"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         Predicates.alwaysTrue(),
-        getProguardConfig(
-            EXAMPLE_KEEP,
-            "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
         options -> {
           configure(options);
           options.enableVerticalClassMerging = false;
@@ -688,12 +740,16 @@
         ImmutableSet.of(
             "classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                getProguardConfig(
+                    EXAMPLE_KEEP,
+                    "-forceinline class classmerging.ProguardMethodMapTest$A { public void"
+                        + " method(); }"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         readProgramFiles(programFiles),
         preservedClassNames::contains,
-        getProguardConfig(
-            EXAMPLE_KEEP,
-            "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
         options -> {
           configure(options);
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
@@ -717,7 +773,13 @@
         ImmutableSet.of(
             "classmerging.SubClassThatReferencesSuperMethod",
             "classmerging.SuperCallRewritingTest");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   // When a subclass A has been merged into its subclass B, we rewrite invoke-super calls that hit
@@ -924,10 +986,11 @@
 
     // Run test.
     runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(String.format("-keep class %s { public static void main(...); }", main)),
         main,
         appBuilder.build(),
-        preservedClassNames::contains,
-        String.format("-keep class %s { public static void main(...); }", main));
+        preservedClassNames::contains);
   }
 
   @Test
@@ -953,10 +1016,12 @@
               "classmerging.SyntheticBridgeSignaturesTest$ASub",
               "classmerging.SyntheticBridgeSignaturesTest$BSub");
       runTestOnInput(
+          testForR8(Backend.DEX)
+              .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+              .allowUnusedProguardConfigurationRules(),
           main,
           readProgramFiles(programFiles),
           preservedClassNames::contains,
-          getProguardConfig(EXAMPLE_KEEP),
           options -> {
             this.configure(options);
             if (!allowInlining) {
@@ -989,7 +1054,13 @@
         ImmutableSet.of(
             "classmerging.ConflictingInterfaceSignaturesTest",
             "classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   // If an exception class A is merged into another exception class B, then all exception tables
@@ -1010,7 +1081,14 @@
             "classmerging.ExceptionTest",
             "classmerging.ExceptionTest$ExceptionB",
             "classmerging.ExceptionTest$Exception2");
-    CodeInspector inspector = runTest(main, programFiles, preservedClassNames::contains);
+    CodeInspector inspector =
+        runTest(
+            testForR8(Backend.DEX)
+                .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+                .allowUnusedProguardConfigurationRules(),
+            main,
+            programFiles,
+            preservedClassNames::contains);
 
     ClassSubject mainClass = inspector.clazz(main);
     assertThat(mainClass, isPresent());
@@ -1061,7 +1139,13 @@
         method.getMethod().getCode().asCfCode().toString(),
         containsString("invokeinterface classmerging.MergeDefaultMethodIntoClassTest$A.f()V"));
 
-    runTestOnInput(main, app, preservedClassNames::contains, getProguardConfig(JAVA8_EXAMPLE_KEEP));
+    runTestOnInput(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        app,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -1079,7 +1163,13 @@
             "classmerging.ClassWithNativeMethodTest",
             "classmerging.ClassWithNativeMethodTest$A",
             "classmerging.ClassWithNativeMethodTest$B");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -1104,7 +1194,13 @@
             "classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl",
             "classmerging.pkg.SimpleInterfaceImplRetriever",
             "classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   @Test
@@ -1130,13 +1226,16 @@
     // Allow access modifications (and prevent SimpleInterfaceImplRetriever from being removed as
     // a result of inlining).
     runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                getProguardConfig(
+                    EXAMPLE_KEEP,
+                    "-allowaccessmodification",
+                    "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         programFiles,
-        preservedClassNames::contains,
-        getProguardConfig(
-            EXAMPLE_KEEP,
-            "-allowaccessmodification",
-            "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"));
+        preservedClassNames::contains);
   }
 
   // TODO(christofferqa): This test checks that the invoke-super instruction in B is not rewritten
@@ -1158,11 +1257,14 @@
             "classmerging.RewritePinnedMethodTest$A",
             "classmerging.RewritePinnedMethodTest$C");
     runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(
+                getProguardConfig(
+                    EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"))
+            .allowUnusedProguardConfigurationRules(),
         main,
         programFiles,
-        preservedClassNames::contains,
-        getProguardConfig(
-            EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"));
+        preservedClassNames::contains);
   }
 
   @Test
@@ -1177,57 +1279,60 @@
     Set<String> preservedClassNames =
         ImmutableSet.of(
             "classmerging.TemplateMethodTest", "classmerging.TemplateMethodTest$AbstractClassImpl");
-    runTest(main, programFiles, preservedClassNames::contains);
+    runTest(
+        testForR8(Backend.DEX)
+            .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
+            .allowUnusedProguardConfigurationRules(),
+        main,
+        programFiles,
+        preservedClassNames::contains);
   }
 
   private CodeInspector runTest(
-      String main, Path[] programFiles, Predicate<String> preservedClassNames) throws Throwable {
-    return runTest(main, programFiles, preservedClassNames, getProguardConfig(EXAMPLE_KEEP));
-  }
-
-  private CodeInspector runTest(
+      R8FullTestBuilder builder,
       String main,
       Path[] programFiles,
-      Predicate<String> preservedClassNames,
-      String proguardConfig)
+      Predicate<String> preservedClassNames)
       throws Throwable {
-    return runTestOnInput(
-        main, readProgramFiles(programFiles), preservedClassNames, proguardConfig);
+    return runTestOnInput(builder, main, readProgramFiles(programFiles), preservedClassNames);
   }
 
   private CodeInspector runTestOnInput(
-      String main, AndroidApp input, Predicate<String> preservedClassNames, String proguardConfig)
+      R8FullTestBuilder builder,
+      String main,
+      AndroidApp input,
+      Predicate<String> preservedClassNames)
       throws Throwable {
-    return runTestOnInput(main, input, preservedClassNames, proguardConfig, this::configure);
+    return runTestOnInput(builder, main, input, preservedClassNames, this::configure);
   }
 
   private CodeInspector runTestOnInput(
+      R8FullTestBuilder builder,
       String main,
       AndroidApp input,
       Predicate<String> preservedClassNames,
-      String proguardConfig,
       Consumer<InternalOptions> optionsConsumer)
       throws Throwable {
     return runTestOnInput(
+        builder,
         main,
         input,
         preservedClassNames,
-        proguardConfig,
         optionsConsumer,
         new VerticalClassMergerDebugTest(main));
   }
 
   private CodeInspector runTestOnInput(
+      R8FullTestBuilder builder,
       String main,
       AndroidApp input,
       Predicate<String> preservedClassNames,
-      String proguardConfig,
       Consumer<InternalOptions> optionsConsumer,
       VerticalClassMergerDebugTest debugTestRunner)
       throws Throwable {
     Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
     R8TestCompileResult compileResult =
-        testForR8(Backend.DEX)
+        builder
             .apply(
                 b -> {
                   // Some tests add DEX inputs, so circumvent the check by adding directly to the
@@ -1238,12 +1343,10 @@
                   }
                 })
             .noMinification()
-            .addKeepRules(proguardConfig)
             .enableProguardTestOptions()
             .addOptionsModification(
                 o -> {
                   optionsConsumer.accept(o);
-                  o.testing.allowUnusedProguardConfigurationRules = true;
                   o.proguardMapConsumer = new FileConsumer(proguardMapPath, o.proguardMapConsumer);
                 })
             .compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index edaf321..8334f1d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -62,6 +62,25 @@
             StringUtils.lines("false", "false", "false", "false", "false", "false"));
   }
 
+  @Test
+  public void testCustomCollectionR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .addInnerClasses(CustomCollectionForwardingTest.class)
+        .addKeepMainRule(Executor.class)
+        .setMinApi(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(
+            StringUtils.lines("false", "false", "false", "false", "false", "false"));
+  }
+
   private void assertForwardingMethods(CodeInspector inspector) {
     if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
       return;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 61296c9..628fbfe 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -16,7 +16,9 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,6 +32,26 @@
 
 public class DesugaredLibraryTestBase extends TestBase {
 
+  // For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
+  // above N and O depending if Stream or Time APIs are used), but we need to compile the program
+  // with a minAPI below to force the use of conversions.
+  protected static TestParametersCollection getConversionParametersUpToExcluding(
+      AndroidApiLevel apiLevel) {
+    if (apiLevel == AndroidApiLevel.N) {
+      return getTestParameters()
+          .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+          .withApiLevelsEndingAtExcluding(AndroidApiLevel.N)
+          .build();
+    }
+    if (apiLevel == AndroidApiLevel.O) {
+      return getTestParameters()
+          .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+          .withApiLevelsEndingAtExcluding(AndroidApiLevel.O)
+          .build();
+    }
+    throw new Error("Unsupported conversion parameters");
+  }
+
   protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
     return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
index 7ca5301..e345069 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
@@ -80,10 +80,7 @@
   @Test
   public void testJavaUtilFunctionR8() throws Exception {
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
-    // TODO(b/139398549): Enable test on API 26+.
-    Assume.assumeTrue(parameters.getApiLevel().getLevel() < 26);
     testForR8(parameters.getBackend())
-        // Following two for checkRewrittenArguments.
         .enableInliningAnnotations()
         .noMinification()
         .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index 1cb8f74..579b6f6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -7,15 +7,16 @@
 import static org.hamcrest.core.StringContains.containsString;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Random;
 import java.util.function.IntUnaryOperator;
 import java.util.stream.IntStream;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -25,21 +26,27 @@
 public class APIConversionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters()
-        .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
-        .withApiLevelsEndingAtExcluding(AndroidApiLevel.M)
-        .build();
+  private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "[5, 6, 7]", "$r8$wrapper$java$util$stream$IntStream$-V-WRP", "IntSummaryStatistics");
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
   }
 
-  public APIConversionTest(TestParameters parameters) {
+  public APIConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
 
   @Test
   public void testAPIConversionNoDesugaring() throws Exception {
+    Assume.assumeTrue("No need to test twice", shrinkDesugaredLibrary);
     testForD8()
         .addInnerClasses(APIConversionTest.class)
         .setMinApi(parameters.getApiLevel())
@@ -54,20 +61,41 @@
   }
 
   @Test
-  public void testAPIConversionDesugaring() throws Exception {
+  public void testAPIConversionDesugaringD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addInnerClasses(APIConversionTest.class)
         .setMinApi(parameters.getApiLevel())
-        .enableCoreLibraryDesugaring(parameters.getApiLevel())
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .assertNoMessages()
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
         .run(parameters.getRuntime(), Executor.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines(
-                "[5, 6, 7]",
-                "$r8$wrapper$java$util$stream$IntStream$-V-WRP",
-                "IntSummaryStatistics"));
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testAPIConversionDesugaringR8() throws Exception {
+    Assume.assumeTrue("Invalid runtime library (missing applyAsInt)", false);
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .addInnerClasses(APIConversionTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Executor.class)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .assertNoMessages()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
   static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
index ef30ad1..75d46fe 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
@@ -4,40 +4,99 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
+import java.util.List;
 import java.util.Optional;
 import java.util.OptionalDouble;
 import java.util.OptionalInt;
 import java.util.OptionalLong;
+import org.junit.BeforeClass;
 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 AllOptionalConversionTest extends DesugaredLibraryTestBase {
 
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "Optional[value]",
+          "OptionalDouble[1.0]",
+          "OptionalInt[1]",
+          "OptionalLong[1]",
+          "Optional[value]",
+          "value");
+
+  private static Path CUSTOM_LIB;
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+  }
+
+  public AllOptionalConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  @BeforeClass
+  public static void compileCustomLib() throws Exception {
+    CUSTOM_LIB =
+        testForD8(getStaticTemp())
+            .addProgramClasses(CustomLibClass.class)
+            .setMinApi(MIN_SUPPORTED)
+            .compile()
+            .writeToZip();
+  }
+
   @Test
-  public void testRewrittenAPICalls() throws Exception {
-    Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+  public void testRewrittenAPICallsD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
-        .setMinApi(AndroidApiLevel.B)
+        .setMinApi(parameters.getApiLevel())
         .addProgramClasses(Executor.class)
         .addLibraryClasses(CustomLibClass.class)
-        .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
-        .addRunClasspathFiles(customLib)
-        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines(
-                "Optional[value]",
-                "OptionalDouble[1.0]",
-                "OptionalInt[1]",
-                "OptionalLong[1]",
-                "Optional[value]",
-                "value"));
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testRewrittenAPICallsR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Executor.class)
+        .addProgramClasses(Executor.class)
+        .addLibraryClasses(CustomLibClass.class)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
   static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index db5c661..5a03f73 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -7,12 +7,11 @@
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 
-import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestDiagnosticMessages;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.time.Duration;
@@ -21,35 +20,94 @@
 import java.time.MonthDay;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.util.List;
+import org.junit.BeforeClass;
 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 AllTimeConversionTest extends DesugaredLibraryTestBase {
 
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+  private static final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "1970-01-02T00:00Z[GMT]",
+          "PT0.000012345S",
+          "GMT",
+          "--03-02",
+          "-1000000000-01-01T00:00:00.999999999Z",
+          "GMT",
+          "GMT");
+
+  private static Path CUSTOM_LIB;
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+  }
+
+  public AllTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  @BeforeClass
+  public static void compileCustomLib() throws Exception {
+    CUSTOM_LIB =
+        testForD8(getStaticTemp())
+            .addProgramClasses(CustomLibClass.class)
+            .setMinApi(MIN_SUPPORTED)
+            .compile()
+            .writeToZip();
+  }
+
   @Test
-  public void testRewrittenAPICalls() throws Exception {
-    Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
-    D8TestCompileResult compileResult =
-        testForD8()
-            .setMinApi(AndroidApiLevel.B)
-            .addProgramClasses(Executor.class)
-            .addLibraryClasses(CustomLibClass.class)
-            .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
-            .enableCoreLibraryDesugaring(AndroidApiLevel.B)
-            .compile();
-    compileResult
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
-        .addRunClasspathFiles(customLib)
-        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
-        .assertSuccessWithOutput(
-            StringUtils.lines(
-                "1970-01-02T00:00Z[GMT]",
-                "PT0.000012345S",
-                "GMT",
-                "--03-02",
-                "-1000000000-01-01T00:00:00.999999999Z",
-                "GMT",
-                "GMT"));
-    assertTrackedAPIS(compileResult.getDiagnosticMessages());
+  public void testRewrittenAPICallsD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForD8()
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(Executor.class)
+        .addLibraryClasses(CustomLibClass.class)
+        .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .inspectDiagnosticMessages(this::assertTrackedAPIS)
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testRewrittenAPICallsR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Executor.class)
+        .addProgramClasses(Executor.class)
+        .addLibraryClasses(CustomLibClass.class)
+        .allowDiagnosticWarningMessages()
+        .addOptionsModification(options -> options.testing.trackDesugaredAPIConversions = true)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
   private void assertTrackedAPIS(TestDiagnosticMessages diagnosticMessages) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index f31ad69..4ac3ccc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -4,33 +4,92 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
 import java.time.MonthDay;
+import java.util.List;
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 // Longs and double take two stack indexes, this had to be dealt with in
 // CfAPIConverter*WrapperCodeProvider (See stackIndex vs index), this class tests that the
 // synthetic Cf code is correct.
+@RunWith(Parameterized.class)
 public class BasicLongDoubleConversionTest extends DesugaredLibraryTestBase {
 
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+  private static final String EXPECTED_RESULT = StringUtils.lines("--01-16");
+
+  private static Path CUSTOM_LIB;
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+  }
+
+  public BasicLongDoubleConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
+  }
+
+  @BeforeClass
+  public static void compileCustomLib() throws Exception {
+    CUSTOM_LIB =
+        testForD8(getStaticTemp())
+            .addProgramClasses(CustomLibClass.class)
+            .setMinApi(MIN_SUPPORTED)
+            .compile()
+            .writeToZip();
+  }
+
   @Test
-  public void testRewrittenAPICalls() throws Exception {
-    Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+  public void testRewrittenAPICallsD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
-        .setMinApi(AndroidApiLevel.B)
+        .setMinApi(parameters.getApiLevel())
         .addProgramClasses(Executor.class)
         .addLibraryClasses(CustomLibClass.class)
-        .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
-        .addRunClasspathFiles(customLib)
-        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
-        .assertSuccessWithOutput(StringUtils.lines("--01-16"));
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  @Test
+  public void testRewrittenAPICallsR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(Executor.class)
+        .addKeepMainRule(Executor.class)
+        .addLibraryClasses(CustomLibClass.class)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .addRunClasspathFiles(CUSTOM_LIB)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
   static class Executor {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
index 1e66f26..bb728fb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
@@ -4,62 +4,85 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.L8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
 import java.time.ZoneId;
+import java.util.List;
 import java.util.TimeZone;
 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 BasicTimeConversionTest extends DesugaredLibraryTestBase {
 
-  @Test
-  public void testTimeGeneratedDex() throws Exception {
-    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
-    Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
-    L8Command.Builder l8Builder =
-        L8Command.builder(diagnosticsHandler)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
-            .addDesugaredLibraryConfiguration(
-                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
-            .setMinApiLevel(AndroidApiLevel.B.getLevel())
-            .setOutput(desugaredLib, OutputMode.DexIndexed);
-    ToolHelper.runL8(l8Builder.build(), x -> {});
-    this.checkTimeConversionGeneratedDex(new CodeInspector(desugaredLib));
+  private final TestParameters parameters;
+  private final boolean shrinkDesugaredLibrary;
+  private static final String GMT = StringUtils.lines("GMT");
+
+  @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getConversionParametersUpToExcluding(AndroidApiLevel.O), BooleanUtils.values());
   }
 
-  private void checkTimeConversionGeneratedDex(CodeInspector inspector) {
-    ClassSubject clazz = inspector.clazz("j$.time.TimeConversions");
-    assertThat(clazz, isPresent());
-    assertEquals(13, clazz.allMethods().size());
+  public BasicTimeConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+    this.parameters = parameters;
   }
 
   @Test
-  public void testRewrittenAPICalls() throws Exception {
+  public void testRewrittenAPICallsD8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
-        .setMinApi(AndroidApiLevel.B)
+        .setMinApi(parameters.getApiLevel())
         .addInnerClasses(BasicTimeConversionTest.class)
-        .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
         .inspect(this::checkAPIRewritten)
-        .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
-        .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(GMT);
+    if (shrinkDesugaredLibrary) {
+      checkKeepRules(keepRuleConsumer.get());
+    }
+  }
+
+  private void checkKeepRules(String keepRules) {
+    assertTrue(keepRules.contains("TimeConversion"));
+  }
+
+  @Test
+  public void testRewrittenAPICallsR8() throws Exception {
+    KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Executor.class)
+        .addInnerClasses(BasicTimeConversionTest.class)
+        .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .compile()
+        .addDesugaredCoreLibraryRunClassPath(
+            this::buildDesugaredLibrary,
+            parameters.getApiLevel(),
+            keepRuleConsumer.get(),
+            shrinkDesugaredLibrary)
+        .run(parameters.getRuntime(), Executor.class)
+        .assertSuccessWithOutput(GMT);
+    if (shrinkDesugaredLibrary) {
+      checkKeepRules(keepRuleConsumer.get());
+    }
   }
 
   private void checkAPIRewritten(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
new file mode 100644
index 0000000..43d5b9a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConversionsPresentTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public ConversionsPresentTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testConversionsDex() throws Exception {
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
+    L8Command.Builder l8Builder =
+        L8Command.builder(diagnosticsHandler)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+            .addDesugaredLibraryConfiguration(
+                StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+            .setMinApiLevel(parameters.getApiLevel().getLevel())
+            .setOutput(desugaredLib, OutputMode.DexIndexed);
+    ToolHelper.runL8(l8Builder.build(), x -> {});
+    this.checkConversionGeneratedDex(new CodeInspector(desugaredLib));
+  }
+
+  private void checkConversionGeneratedDex(CodeInspector inspector) {
+    List<FoundClassSubject> conversionsClasses =
+        inspector.allClasses().stream()
+            .filter(c -> c.getOriginalName().contains("Conversions"))
+            .collect(Collectors.toList());
+    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+      assertEquals(5, conversionsClasses.size());
+      assertTrue(inspector.clazz("j$.util.OptionalConversions").isPresent());
+      assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.LongSummaryStatisticsConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.IntSummaryStatisticsConversions").isPresent());
+      assertTrue(inspector.clazz("j$.util.DoubleSummaryStatisticsConversions").isPresent());
+    } else if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+      assertEquals(1, conversionsClasses.size());
+      assertTrue(inspector.clazz("j$.time.TimeConversions").isPresent());
+    } else {
+      assertEquals(0, inspector.allClasses().size());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index dbdc654..a79ccf7 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.desugar.nestaccesscontrol;
 
 import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -50,7 +51,9 @@
         .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
         .addKeepRuleFiles(MAIN_KEEP)
         .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+        .allowDiagnosticWarningMessages()
         .compile()
+        .assertAllWarningMessagesMatch(containsString("Missing class:"))
         .inspect(this::assertNotEmpty)
         .inspect(Java11R8CompilationTest::assertNoNests);
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 48fe91b..75982e9 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -69,7 +69,11 @@
   }
 
   private TestCompileResult compileOnlyClassesMatching(
-      Matcher<String> matcher, boolean d8, boolean ignoreMissingClasses) throws Exception {
+      Matcher<String> matcher,
+      boolean d8,
+      boolean allowDiagnosticWarningMessages,
+      boolean ignoreMissingClasses)
+      throws Exception {
     List<Path> matchingClasses =
         CLASS_NAMES.stream()
             .filter(matcher::matches)
@@ -93,6 +97,7 @@
                 options.enableNestBasedAccessDesugaring = true;
                 options.ignoreMissingClasses = ignoreMissingClasses;
               })
+          .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
           .compile();
     }
   }
@@ -101,7 +106,7 @@
     try {
       Matcher<String> innerClassMatcher =
           containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
-      compileOnlyClassesMatching(innerClassMatcher, false, false);
+      compileOnlyClassesMatching(innerClassMatcher, false, false, false);
       fail("Should have raised an exception for missing nest host");
     } catch (Exception e) {
       assertTrue(e.getCause().getCause().getMessage().contains("requires its nest host"));
@@ -111,7 +116,7 @@
   private void testIncompleteNestError() {
     try {
       Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
-      compileOnlyClassesMatching(innerClassMatcher, false, false);
+      compileOnlyClassesMatching(innerClassMatcher, false, false, false);
       fail("Should have raised an exception for incomplete nest");
     } catch (Exception e) {
       assertTrue(e.getCause().getCause().getMessage().contains("requires its nest mates"));
@@ -121,7 +126,7 @@
   private void testMissingNestHostWarning(boolean d8, boolean desugarWarning) throws Exception {
     Matcher<String> innerClassMatcher =
         containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
-    TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
+    TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
     assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
     if (desugarWarning) {
       assertTrue(
@@ -137,7 +142,7 @@
 
   private void testIncompleteNestWarning(boolean d8, boolean desugarWarning) throws Exception {
     Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
-    TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
+    TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true);
     assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
     if (desugarWarning) {
       assertTrue(
@@ -145,9 +150,7 @@
               .anyMatch(warn -> warn instanceof IncompleteNestNestDesugarDiagnosic));
     } else if (!d8) {
       // R8 should raise extra warning when cleaning the nest.
-      assertTrue(
-          compileResult.getDiagnosticMessages().getWarnings().stream()
-              .anyMatch(warn -> warn.getDiagnosticMessage().contains("requires its nest mates")));
+      compileResult.assertWarningMessageThatMatches(containsString("requires its nest mates"));
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
index 247e8a1..9c4872f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingAnalysisTest.java
@@ -59,6 +59,7 @@
             .enableInliningAnnotations()
             .addKeepRules(KEEP_ENUM)
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(this::assertEnumsAsExpected);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
index 762380a..e4f7bd1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
@@ -46,16 +45,13 @@
 
   @Test
   public void testEnumUnboxingFailure() throws Exception {
-    R8FullTestBuilder r8FullTestBuilder =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(FailingMethodEnumUnboxingAnalysisTest.class);
-    for (Class<?> failure : FAILURES) {
-      r8FullTestBuilder.addKeepMainRule(failure);
-    }
     R8TestCompileResult compile =
-        r8FullTestBuilder
+        testForR8(parameters.getBackend())
+            .addInnerClasses(FailingMethodEnumUnboxingAnalysisTest.class)
+            .addKeepMainRules(FAILURES)
             .addKeepRules(KEEP_ENUM)
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .enableInliningAnnotations()
             .addOptionsModification(
                 // Disabled to avoid toString() being removed.
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
index 2841689..8811ee5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
@@ -40,6 +40,7 @@
             .addKeepMainRules(INPUTS)
             .addKeepRules(KEEP_ENUM)
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .enableInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
             .noMinification()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
index 079c9f7..2570a12 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingAnalysisTest.java
@@ -37,6 +37,7 @@
             .addKeepMainRule(classToTest)
             .addKeepRules(KEEP_ENUM)
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
index 87cacd5..2abd358 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PhiEnumUnboxingAnalysisTest.java
@@ -39,6 +39,7 @@
             .addKeepRules(KEEP_ENUM)
             .enableInliningAnnotations()
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
index 699c72d..53cddc6 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingAnalysisTest.java
@@ -39,6 +39,7 @@
             .addKeepRules(KEEP_ENUM)
             .enableInliningAnnotations()
             .addOptionsModification(this::enableEnumOptions)
+            .allowDiagnosticInfoMessages()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspectDiagnosticMessages(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 6bf5d8f..0788985 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8.internal.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -91,10 +93,15 @@
                   options.enableStringSwitchConversion = true;
                 })
             .allowAccessModification()
+            .allowDiagnosticMessages()
             .allowUnusedProguardConfigurationRules()
             .enableInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
             .compile()
+            .assertAllInfoMessagesMatch(
+                containsString("Proguard configuration rule does not match anything"))
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
             .inspect(this::inspect);
 
     for (String main : mains) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 558664f..1473e03 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -5,6 +5,9 @@
 package com.android.tools.r8.internal.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -88,10 +91,15 @@
                   options.enableStringSwitchConversion = true;
                 })
             .allowAccessModification(allowAccessModification)
+            .allowDiagnosticMessages()
             .allowUnusedProguardConfigurationRules()
             .minification(enableMinification)
             .setMinApi(parameters.getApiLevel())
             .compile()
+            .assertAllInfoMessagesMatch(
+                containsString("Proguard configuration rule does not match anything"))
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
             .inspect(
                 outputInspector -> {
                   verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
@@ -356,10 +364,17 @@
               options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
             })
         .allowAccessModification(allowAccessModification)
+        .allowDiagnosticMessages()
         .allowUnusedProguardConfigurationRules()
         .minification(enableMinification)
         .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllInfoMessagesMatch(
+            containsString("Proguard configuration rule does not match anything"))
+        .assertAllWarningMessagesMatch(
+            anyOf(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+                containsString("required for default or static interface methods desugaring")))
         .inspect(
             inspector ->
                 assertRewrittenProtoSchemasMatch(new CodeInspector(PROGRAM_FILES), inspector));
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index faa91e5..7a261ff 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -5,6 +5,9 @@
 package com.android.tools.r8.internal.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -64,10 +67,14 @@
               options.enableStringSwitchConversion = true;
             })
         .allowAccessModification(allowAccessModification)
+        .allowDiagnosticMessages()
         .allowUnusedProguardConfigurationRules()
         .minification(enableMinification)
         .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllInfoMessagesMatch(
+            containsString("Proguard configuration rule does not match anything"))
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .inspect(
             outputInspector -> {
               verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
@@ -113,10 +120,17 @@
               options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
             })
         .allowAccessModification(allowAccessModification)
+        .allowDiagnosticMessages()
         .allowUnusedProguardConfigurationRules()
         .minification(enableMinification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllInfoMessagesMatch(
+            containsString("Proguard configuration rule does not match anything"))
+        .assertAllWarningMessagesMatch(
+            anyOf(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+                containsString("required for default or static interface methods desugaring")))
         .inspect(
             inspector ->
                 assertRewrittenProtoSchemasMatch(new CodeInspector(PROGRAM_FILES), inspector));
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 02395ef..979de40 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -81,14 +82,16 @@
   public void testOptimizationInfo() throws Exception {
     AppView<AppInfoWithSubtyping> appView = buildApp();
     OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
-    FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis(appView);
+    FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
+    FieldAccessAnalysis fieldAccessAnalysis =
+        new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis);
 
     DexProgramClass clazz = appView.appInfo().classes().iterator().next();
     assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString());
 
     for (DexEncodedMethod method : clazz.methods()) {
       IRCode code = method.buildIR(appView, Origin.unknown());
-      fieldBitAccessAnalysis.recordFieldAccesses(code, feedback);
+      fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock());
     }
 
     int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
@@ -205,6 +208,19 @@
     }
   }
 
+  static class MethodProcessorMock implements MethodProcessor {
+
+    @Override
+    public Phase getPhase() {
+      return Phase.PRIMARY;
+    }
+
+    @Override
+    public boolean isProcessedConcurrently(DexEncodedMethod method) {
+      return false;
+    }
+  }
+
   static class OptimizationFeedbackMock extends OptimizationFeedbackIgnore {
 
     Reference2IntMap<DexEncodedField> bitsReadPerField = new Reference2IntOpenHashMap<>();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
index 901a577..41ce8a8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
@@ -5,19 +5,20 @@
 package com.android.tools.r8.ir.analysis.type;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.NeverMerge;
-import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.base.Throwables;
-import java.nio.file.Path;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -35,7 +36,8 @@
 
   @Parameters(name = "{1}, allow type errors: {0}")
   public static List<Object[]> data() {
-    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   public MissingClassesJoinTest(boolean allowTypeErrors, TestParameters parameters) {
@@ -45,57 +47,63 @@
 
   @Test
   public void test() throws Exception {
-    Path classpathFile =
-        testForD8()
-            .addProgramClasses(ASub2.class)
-            .setMinApi(parameters.getRuntime())
-            .compile()
-            .writeToZip();
-
     if (parameters.isDexRuntime() && !allowTypeErrors) {
-      testForD8()
-          // Intentionally not adding ASub2 as a program class.
-          .addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
-          .setMinApi(parameters.getRuntime())
-          .compile()
-          .addRunClasspathFiles(classpathFile)
+      D8TestCompileResult compileResult =
+          testForD8()
+              // Intentionally not adding ASub2 as a program class.
+              .addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
+              .setMinApi(parameters.getApiLevel())
+              .compile();
+
+      testForRuntime(parameters)
+          .addProgramFiles(compileResult.writeToZip())
+          .addProgramClasses(ASub2.class)
           .run(parameters.getRuntime(), TestClass.class)
           .assertSuccessWithOutput(expectedOutput);
     }
 
     try {
-      R8TestRunResult result =
+      R8TestCompileResult compileResult =
           testForR8(parameters.getBackend())
               // Intentionally not adding ASub2 as a program class.
               .addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
               .addKeepAllClassesRule()
               .addOptionsModification(options -> options.testing.allowTypeErrors = allowTypeErrors)
+              .allowDiagnosticWarningMessages()
               .enableMergeAnnotations()
-              .setMinApi(parameters.getRuntime())
+              .setMinApi(parameters.getApiLevel())
               .compile()
-              .addRunClasspathFiles(classpathFile)
-              .run(parameters.getRuntime(), TestClass.class);
+              .assertAllWarningMessagesMatch(
+                  equalTo(
+                      "The method `void "
+                          + TestClass.class.getTypeName()
+                          + ".main(java.lang.String[])` does not type check and will be assumed to"
+                          + " be unreachable."));
 
       // Compilation fails unless type errors are allowed.
       assertTrue(allowTypeErrors);
 
-      // TestClass.main() does not type check, so it should have been replaced by `throw null`.
-      // Note that, even if we do not replace the body of main() with `throw null`, the code would
-      // still not work for the CF backend:
-      //
-      //     java.lang.VerifyError: Bad type on operand stack
-      //     Exception Details:
-      //       Location:
-      //         MissingClassesJoinTest$TestClass.main([Ljava/lang/String;)V @28: putstatic
-      //       Reason:
-      //         Type 'java/lang/Object' (current frame, stack[0]) is not assignable to
-      //         'com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest$A'
-      //       Current Frame:
-      //         bci: @28
-      //         flags: { }
-      //         locals: { 'java/lang/Object' }
-      //         stack: { 'java/lang/Object' }
-      result.assertFailureWithErrorThatMatches(containsString("NullPointerException"));
+      testForRuntime(parameters)
+          .addProgramFiles(compileResult.writeToZip())
+          .addProgramClasses(ASub2.class)
+          .run(parameters.getRuntime(), TestClass.class)
+          // TestClass.main() does not type check, so it should have been replaced by `throw null`.
+          // Note that, even if we do not replace the body of main() with `throw null`, the code
+          // would still not work for the CF backend:
+          //
+          //     java.lang.VerifyError: Bad type on operand stack
+          //     Exception Details:
+          //       Location:
+          //         MissingClassesJoinTest$TestClass.main([Ljava/lang/String;)V @28: putstatic
+          //       Reason:
+          //         Type 'java/lang/Object' (current frame, stack[0]) is not assignable to
+          //         'com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest$A'
+          //       Current Frame:
+          //         bci: @28
+          //         flags: { }
+          //         locals: { 'java/lang/Object' }
+          //         stack: { 'java/lang/Object' }
+          .assertFailureWithErrorThatMatches(containsString("NullPointerException"));
     } catch (CompilationFailedException e) {
       // Compilation should only fail when type errors are not allowed.
       assertFalse(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
index a1042ce..73d694e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithInheritanceTest.java
@@ -40,10 +40,9 @@
         testForR8(parameters.getBackend())
             .addInnerClasses(BuilderWithInheritanceTest.class)
             .addKeepMainRule(TestClass.class)
-            // TODO(b/145090972): Should never need to exit gracefully during testing.
-            .allowClassInlinerGracefulExit()
             .enableInliningAnnotations()
             .enableMergeAnnotations()
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput("42")
             .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
index 6d70ba5..34e9d5d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.java
@@ -40,7 +40,6 @@
             ClassInlineInstanceInitializerWithInaccessibleStaticGetTest.class,
             ClassInlineInstanceInitializerWithInaccessibleStaticGetTestClasses.class)
         .addKeepMainRule(TestClass.class)
-        .allowClassInlinerGracefulExit()
         .enableInliningAnnotations()
         .enableMergeAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 08d4f85..98a6e9e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -4,25 +4,21 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
-import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.google.common.collect.Streams;
-import java.util.List;
 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 Regress131349148 extends TestBase {
 
@@ -60,12 +56,18 @@
   public void testNoInlineNonExistingCatchPreL() throws Exception {
     R8TestRunResult result =
         testForR8(parameters.getBackend())
-            .addProgramClasses(TestClassCallingMethodWithNonExisting.class,
-                ClassWithCatchNonExisting.class, ExistingException.class)
+            .addProgramClasses(
+                TestClassCallingMethodWithNonExisting.class,
+                ClassWithCatchNonExisting.class,
+                ExistingException.class)
             .addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
             .addKeepRules("-dontwarn " + NonExistingException.class.getTypeName())
+            .allowDiagnosticWarningMessages(
+                parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
             .setMinApi(parameters.getApiLevel())
             .compile()
+            .assertAllWarningMessagesMatch(
+                containsString("required for default or static interface methods desugaring"))
             .run(parameters.getRuntime(), TestClassCallingMethodWithNonExisting.class)
             .assertSuccess();
     ClassSubject classSubject =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java
new file mode 100644
index 0000000..5dc83d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/PrunedClassNameComparisonTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class PrunedClassNameComparisonTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public PrunedClassNameComparisonTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(PrunedClassNameComparisonTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Live!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+    assertThat(testClassSubject, isPresent());
+    assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+    assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      if (get()
+          .getClass()
+          .getName()
+          .equals("com.android.tools.r8.ir.optimize.library.PrunedClassNameComparisonTest$C")) {
+        dead();
+      } else {
+        live();
+      }
+    }
+
+    static I get() {
+      return System.currentTimeMillis() >= 0 ? new A() : new B();
+    }
+
+    @NeverInline
+    static void live() {
+      System.out.println("Live!");
+    }
+
+    @NeverInline
+    static void dead() {
+      System.out.println("Dead!");
+    }
+  }
+
+  interface I {}
+
+  static class A implements I {}
+
+  static class B implements I {}
+
+  static class C implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java
new file mode 100644
index 0000000..3eb649a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldWithDefaultValueAssignmentTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public FieldWithDefaultValueAssignmentTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(FieldWithDefaultValueAssignmentTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Live!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+    assertThat(testClassSubject, isPresent());
+    assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
+    assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      Config config = Config.create();
+      if (config.alwaysFalse) {
+        dead();
+      } else {
+        live();
+      }
+    }
+
+    @NeverInline
+    static void live() {
+      System.out.println("Live!");
+    }
+
+    @NeverInline
+    static void dead() {
+      System.out.println("Dead!");
+    }
+  }
+
+  static class Config {
+
+    boolean alwaysFalse;
+
+    @NeverInline
+    static Config create() {
+      Config config = new Config();
+      config.alwaysFalse = false;
+      return config;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
index 4e4624a..86745e5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
@@ -4,10 +4,13 @@
 
 package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -28,6 +31,7 @@
     return getTestParameters()
         .withCfRuntimes()
         .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+        .withApiLevelsStartingAtIncluding(AndroidApiLevel.O)
         .build();
   }
 
@@ -40,8 +44,10 @@
     testForR8(parameters.getBackend())
         .addProgramClassFileData(InstanceFieldLoadsSeparatedByInvokeCustomTestClassGenerator.dump())
         .addKeepAllClassesRule()
-        .setMinApi(parameters.getRuntime())
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllWarningMessagesMatch(containsString("Unknown bootstrap method"))
         .run(parameters.getRuntime(), "InstanceFieldLoadsSeparatedByInvokeCustomTestClass")
         .assertSuccess();
   }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
index 8896197..8b33acb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -143,7 +143,7 @@
   @Parameters(name = "{0} minify:{1} {2}")
   public static Collection<Object[]> parameters() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(),
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
         BooleanUtils.values(),
         TestNamingConfig.values());
   }
@@ -159,6 +159,23 @@
     this.config = config;
   }
 
+  public boolean hasMalformedInnerClassAttribute() {
+    switch (config) {
+      case DEFAULT:
+      case OUTER_ENDS_WITH_DOLLAR:
+      case $_$_$:
+      case DOLLAR2_SEPARATOR:
+        return false;
+      case EMTPY_SEPARATOR:
+      case UNDERBAR_SEPARATOR:
+      case NON_NESTED_INNER:
+      case WRONG_REPACKAGE:
+        return true;
+      default:
+        throw new Unreachable("Unexpected test configuration: " + config);
+    }
+  }
+
   private void checkWarningsAboutMalformedAttribute(TestCompileResult<?, ?> result) {
     switch (config) {
       case DEFAULT:
@@ -188,7 +205,7 @@
         testForD8()
             .addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
             .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .compile();
     checkWarningsAboutMalformedAttribute(d8CompileResult);
     D8TestRunResult d8RunResult = d8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
@@ -211,11 +228,13 @@
             .addKeepRules("-keep,allowobfuscation class * { *; }")
             .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
             .addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
+            .allowDiagnosticInfoMessages(hasMalformedInnerClassAttribute())
             .minification(minify)
             .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
-            .setMinApi(parameters.getRuntime())
-            .compile();
-    checkWarningsAboutMalformedAttribute(r8CompileResult);
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .apply(this::checkWarningsAboutMalformedAttribute);
+
     CodeInspector inspector = r8CompileResult.inspector();
     R8TestRunResult r8RunResult = r8CompileResult.run(parameters.getRuntime(), MAIN_CLASS);
     switch (config) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
index c13b942..c8d3f1c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.JvmTestRunResult;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestBase;
@@ -22,28 +23,21 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class StringLengthTest extends TestBase {
-  private static final String JAVA_OUTPUT = StringUtils.lines(
-      "4",
-      "6",
-      "Shared",
-      "14",
-      "Another_shared",
-      "2",
-      "1",
-      "𐀀", // Different output in Windows.
-      "3"
-  );
+  private static final String JAVA_OUTPUT =
+      StringUtils.lines("4", "6", "Shared", "14", "Another_shared", "2", "1", "🂡", "3");
   private static final Class<?> MAIN = TestClass.class;
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   private final TestParameters parameters;
@@ -55,12 +49,10 @@
   @Test
   public void testJVMOutput() throws Exception {
     assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime());
-    // TODO(b/119097175)
+    JvmTestRunResult run = testForJvm().addTestClasspath().run(parameters.getRuntime(), MAIN);
+    // TODO(b/119097175): Fix test
     if (!ToolHelper.isWindows()) {
-      testForJvm()
-          .addTestClasspath()
-          .run(parameters.getRuntime(), MAIN)
-          .assertSuccessWithOutput(JAVA_OUTPUT);
+      run.assertSuccessWithOutput(JAVA_OUTPUT);
     }
   }
 
@@ -83,30 +75,23 @@
   @Test
   public void testD8() throws Exception {
     assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
-
     D8TestRunResult result =
         testForD8()
             .release()
             .addProgramClasses(MAIN)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN);
-    // TODO(b/119097175)
-    if (!ToolHelper.isWindows()) {
-      result.assertSuccessWithOutput(JAVA_OUTPUT);
-    }
-    test(result, 1, 4);
+    result.assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 1, 5);
 
     result =
         testForD8()
             .debug()
             .addProgramClasses(MAIN)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN);
-    // TODO(b/119097175)
-    if (!ToolHelper.isWindows()) {
-      result.assertSuccessWithOutput(JAVA_OUTPUT);
-    }
-    test(result, 6, 0);
+    result.assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 6, 1);
   }
 
   @Test
@@ -116,16 +101,16 @@
             .addProgramClasses(MAIN)
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN);
-    // TODO(b/119097175)
+    // TODO(b/119097175): Fix test
     if (!ToolHelper.isWindows()) {
       result.assertSuccessWithOutput(JAVA_OUTPUT);
+      test(result, 0, parameters.isDexRuntime() ? 6 : 7);
     }
-    test(result, 0, parameters.isDexRuntime() ? 5 : 6);
   }
 
-  static class TestClass {
+  public static class TestClass {
 
     @ForceInline
     static String simpleInlineable() {
@@ -139,7 +124,7 @@
       return n.length();
     }
 
-    public static void main(String[] args) {
+    public static void main(String[] args) throws UnsupportedEncodingException {
       String s1 = "GONE";
       // Can be computed at compile time: constCount++
       System.out.println(s1.length());
@@ -155,12 +140,14 @@
       System.out.println(s4.length());
       System.out.println(s4);
 
-      String s5 = "\uD800\uDC00";  // U+10000
+      String s5 = "\uD83C\uDCA1"; // U+1F0A1
       // Can be computed at compile time: constCount++
       System.out.println(s5.length());
       // Even reusable: should not increase any counts.
       System.out.println(s5.codePointCount(0, s5.length()));
-      System.out.println(s5);
+      // The true below will add to the constant count.
+      PrintStream ps = new PrintStream(System.out, true, "UTF-8");
+      ps.println(s5);
 
       // Make sure this is not optimized in DEBUG mode.
       int l = "ABC".length();
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 1c46c19..4062cb0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8.kotlin.lambda;
 
 import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestParameters;
@@ -54,11 +56,14 @@
         .addLibraryFiles(ToolHelper.getKotlinStdlibJar())
         .addProgramFiles(ktClasses)
         .addKeepMainRule("**.B143165163Kt")
+        .allowDiagnosticInfoMessages()
         .setMinApi(parameters.getApiLevel())
         .compile()
         // TODO(b/143165163): better not output info like this.
-        .assertInfoMessageThatMatches(containsString("Unrecognized Kotlin lambda"))
-        .assertInfoMessageThatMatches(containsString("unexpected static method"))
+        .assertAllInfoMessagesMatch(
+            allOf(
+                containsString("Unrecognized Kotlin lambda"),
+                containsString("unexpected static method")))
         .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar())
         .run(parameters.getRuntime(), pkg + ".B143165163Kt")
         .assertSuccessWithOutputLines("outer foo bar", "outer foo default");
@@ -80,11 +85,15 @@
         .addProgramFiles(ToolHelper.getKotlinStdlibJar())
         .addProgramFiles(ktClasses)
         .addKeepMainRule("**.B143165163Kt")
+        .allowDiagnosticMessages()
         .setMinApi(parameters.getApiLevel())
         .compile()
         // TODO(b/143165163): better not output info like this.
-        .assertInfoMessageThatMatches(containsString("Unrecognized Kotlin lambda"))
-        .assertInfoMessageThatMatches(containsString("does not implement any interfaces"))
+        .assertAllInfoMessagesMatch(
+            allOf(
+                containsString("Unrecognized Kotlin lambda"),
+                containsString("does not implement any interfaces")))
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), pkg + ".B143165163Kt")
         .assertSuccessWithOutputLines("outer foo bar", "outer foo default");
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index d866cca..562fdbb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -13,7 +13,6 @@
     super(targetVersion);
   }
 
-  static final String PKG_PREFIX =
-      DescriptorUtils.getBinaryNameFromJavaType(
-          KotlinMetadataTestBase.class.getPackage().getName());
+  static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
+  static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
deleted file mode 100644
index e785965..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
+++ /dev/null
@@ -1,191 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInClasspathTypeTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInClasspathTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path baseLibJar;
-  private static Path extLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String baseLibFolder = PKG_PREFIX + "/classpath_lib_base";
-    baseLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(baseLibFolder, "itf"))
-            .compile();
-    String extLibFolder = PKG_PREFIX + "/classpath_lib_ext";
-    extLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(baseLibJar)
-            .addSourceFiles(getKotlinFileInTest(extLibFolder, "impl"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInClasspathType_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addClasspathFiles(baseLibJar)
-            .addProgramFiles(extLibJar)
-            // Keep the Extra class and its interface (which has the method).
-            .addKeepRules("-keep class **.Extra")
-            // Keep the ImplKt extension method which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.ImplKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String implClassName = pkg + ".classpath_lib_ext.Impl";
-    final String implKtClassName = pkg + ".classpath_lib_ext.ImplKt";
-    final String extraClassName = pkg + ".classpath_lib_ext.Extra";
-    compileResult.inspect(inspector -> {
-      assertThat(inspector.clazz(implClassName), not(isPresent()));
-
-      ClassSubject implKt = inspector.clazz(implKtClassName);
-      assertThat(implKt, isPresent());
-      assertThat(implKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = implKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
-      assertThat(kmFunction, isPresent());
-
-      ClassSubject extra = inspector.clazz(extraClassName);
-      assertThat(extra, isPresent());
-      assertThat(extra, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = extra.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Impl")));
-      // Can't build ClassSubject with Itf in classpath. Instead, check if the reference to Itf is
-      // not altered via descriptors.
-      List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
-      assertTrue(superTypeDescriptors.stream().noneMatch(supertype -> supertype.contains("Impl")));
-      assertTrue(superTypeDescriptors.stream().anyMatch(supertype -> supertype.contains("Itf")));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/classpath_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(baseLibJar, libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".classpath_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::foo");
-  }
-
-  @Test
-  public void testMetadataInClasspathType_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addClasspathFiles(baseLibJar)
-            .addProgramFiles(extLibJar)
-            // Keep the Extra class and its interface (which has the method).
-            .addKeepRules("-keep class **.Extra")
-            // Keep Super, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Impl")
-            // Keep the ImplKt extension method which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.ImplKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String implClassName = pkg + ".classpath_lib_ext.Impl";
-    final String implKtClassName = pkg + ".classpath_lib_ext.ImplKt";
-    final String extraClassName = pkg + ".classpath_lib_ext.Extra";
-    compileResult.inspect(inspector -> {
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isRenamed());
-
-      ClassSubject implKt = inspector.clazz(implKtClassName);
-      assertThat(implKt, isPresent());
-      assertThat(implKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = implKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
-      assertThat(kmFunction, isExtensionFunction());
-
-      ClassSubject extra = inspector.clazz(extraClassName);
-      assertThat(extra, isPresent());
-      assertThat(extra, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = extra.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Impl")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(impl.getFinalDescriptor())));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/classpath_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(baseLibJar, libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".classpath_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::foo");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java
deleted file mode 100644
index a5c01a5..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInCompanionTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInCompanionTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInCompanionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path companionLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String companionLibFolder = PKG_PREFIX + "/companion_lib";
-    companionLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(companionLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInCompanion_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(companionLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep getters for B$Companion.(singleton|foo) which will be referenced at the app.
-            .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
-            // No rule for Super, but will be kept and renamed.
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".companion_lib.Super";
-    final String bClassName = pkg + ".companion_lib.B";
-    final String companionClassName = pkg + ".companion_lib.B$Companion";
-    compileResult.inspect(inspector -> {
-      ClassSubject sup = inspector.clazz(superClassName);
-      assertThat(sup, isRenamed());
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
-      // Bridge for the property in the companion that needs a backing field.
-      MethodSubject singletonBridge = impl.uniqueMethodWithName("access$getSingleton$cp");
-      assertThat(singletonBridge, isRenamed());
-
-      // For B$Companion.foo, no backing field needed, hence no bridge.
-      MethodSubject fooBridge = impl.uniqueMethodWithName("access$getFoo$cp");
-      assertThat(fooBridge, not(isPresent()));
-
-      ClassSubject companion = inspector.clazz(companionClassName);
-      assertThat(companion, isRenamed());
-
-      MethodSubject singletonGetter = companion.uniqueMethodWithName("getSingleton");
-      assertThat(singletonGetter, isPresent());
-
-      MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo");
-      assertThat(fooGetter, isPresent());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/companion_app";
-    ProcessResult kotlinTestCompileResult =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/143687784): update to just .compile() once fixed.
-            .compileRaw();
-
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: singleton"));
-    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java
deleted file mode 100644
index 0f124e4..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInExtensionFunctionTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInExtensionFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path extLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String extLibFolder = PKG_PREFIX + "/extension_function_lib";
-    extLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInExtensionFunction_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(extLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep the BKt extension function which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".extension_function_lib.Super";
-    final String bClassName = pkg + ".extension_function_lib.B";
-    final String bKtClassName = pkg + ".extension_function_lib.BKt";
-    compileResult.inspect(inspector -> {
-      assertThat(inspector.clazz(superClassName), not(isPresent()));
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
-      assertThat(kmFunction, isExtensionFunction());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/extension_function_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".extension_function_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-
-  @Test
-  public void testMetadataInExtensionFunction_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(extLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep Super, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Super")
-            // Keep the BKt extension function which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".extension_function_lib.Super";
-    final String bClassName = pkg + ".extension_function_lib.B";
-    final String bKtClassName = pkg + ".extension_function_lib.BKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject sup = inspector.clazz(superClassName);
-      assertThat(sup, isPresent());
-      assertThat(sup, isRenamed());
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
-      assertThat(kmFunction, isExtensionFunction());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/extension_function_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".extension_function_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java
deleted file mode 100644
index 84d9dd2..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInExtensionPropertyTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInExtensionPropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path extLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String extLibFolder = PKG_PREFIX + "/extension_property_lib";
-    extLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInExtensionProperty_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(extLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep the BKt extension property which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".extension_property_lib.Super";
-    final String bClassName = pkg + ".extension_property_lib.B";
-    final String bKtClassName = pkg + ".extension_property_lib.BKt";
-    compileResult.inspect(inspector -> {
-      assertThat(inspector.clazz(superClassName), not(isPresent()));
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-      KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName("doStuff");
-      assertThat(kmFunction, isPresent());
-      assertThat(kmFunction, not(isExtensionFunction()));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
-      assertThat(kmProperty, isExtensionProperty());
-      assertNotNull(kmProperty.getterSignature());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/extension_property_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".extension_property_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-
-  @Test
-  public void testMetadataInExtensionProperty_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(extLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep Super, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Super")
-            // Keep the BKt extension property which requires metadata
-            // to be called with Kotlin syntax from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".extension_property_lib.Super";
-    final String bClassName = pkg + ".extension_property_lib.B";
-    final String bKtClassName = pkg + ".extension_property_lib.BKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject sup = inspector.clazz(superClassName);
-      assertThat(sup, isRenamed());
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
-      assertThat(kmProperty, isExtensionProperty());
-      assertNotNull(kmProperty.getterSignature());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/extension_property_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".extension_property_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java
deleted file mode 100644
index c0a1092..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInFunctionTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path funLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String funLibFolder = PKG_PREFIX + "/function_lib";
-    funLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(funLibFolder, "B"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInFunction_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(funLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep the BKt method, which will be called from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".function_lib.Super";
-    final String bClassName = pkg + ".function_lib.B";
-    final String bKtClassName = pkg + ".function_lib.BKt";
-    compileResult.inspect(inspector -> {
-      assertThat(inspector.clazz(superClassName), not(isPresent()));
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
-      assertThat(kmFunction, isPresent());
-      assertThat(kmFunction, not(isExtensionFunction()));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/function_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".function_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-
-  @Test
-  public void testMetadataInFunction_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(funLibJar)
-            // Keep the B class and its interface (which has the doStuff method).
-            .addKeepRules("-keep class **.B")
-            .addKeepRules("-keep class **.I { <methods>; }")
-            // Keep Super, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Super")
-            // Keep the BKt method, which will be called from other kotlin code.
-            .addKeepRules("-keep class **.BKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String superClassName = pkg + ".function_lib.Super";
-    final String bClassName = pkg + ".function_lib.B";
-    final String bKtClassName = pkg + ".function_lib.BKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject sup = inspector.clazz(superClassName);
-      assertThat(sup, isRenamed());
-
-      ClassSubject impl = inspector.clazz(bClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Super")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
-
-      ClassSubject bKt = inspector.clazz(bKtClassName);
-      assertThat(bKt, isPresent());
-      assertThat(bKt, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = bKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
-      assertThat(kmFunction, isPresent());
-      assertThat(kmFunction, not(isExtensionFunction()));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/function_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".function_app.MainKt")
-        .assertSuccessWithOutputLines("do stuff", "do stuff");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java
deleted file mode 100644
index 8917ebe..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInLibraryTypeTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInLibraryTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path baseLibJar;
-  private static Path extLibJar;
-  private static Path appJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String baseLibFolder = PKG_PREFIX + "/libtype_lib_base";
-    baseLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(baseLibFolder, "base"))
-            .compile();
-    String extLibFolder = PKG_PREFIX + "/libtype_lib_ext";
-    extLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(baseLibJar)
-            .addSourceFiles(getKotlinFileInTest(extLibFolder, "ext"))
-            .compile();
-    String appFolder = PKG_PREFIX + "/libtype_app";
-    appJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(baseLibJar)
-            .addClasspathFiles(extLibJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .compile();
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    String pkg = getClass().getPackage().getName();
-    String main = pkg + ".libtype_app.MainKt";
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            // Intentionally not providing basLibJar as lib file nor classpath file.
-            .addProgramFiles(extLibJar, appJar)
-            // Keep Ext extension method which requires metadata to be called with Kotlin syntax
-            // from other kotlin code.
-            .addKeepRules("-keep class **.ExtKt { <methods>; }")
-            // Keep the main entry.
-            .addKeepMainRule(main)
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            // -dontoptimize so that basic code structure is kept.
-            .noOptimization()
-            .compile();
-    final String extClassName = pkg + ".libtype_lib_ext.ExtKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject ext = inspector.clazz(extClassName);
-      assertThat(ext, isPresent());
-      assertThat(ext, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmPackageSubject kmPackage = ext.getKmPackage();
-      assertThat(kmPackage, isPresent());
-      // Type appearance of library type, Base, should be kept, even if it's not provided.
-      // Note that the resulting ClassSubject for Base is an absent one as we don't provide it, and
-      // thus we can't use `getReturnTypesInFunctions`, which filters out absent class subject.
-      assertTrue(kmPackage.getReturnTypeDescriptorsInFunctions().stream().anyMatch(
-          returnType -> returnType.contains("Base")));
-    });
-
-    Path out = compileResult.writeToZip();
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar)
-        .addClasspath(out)
-        .run(parameters.getRuntime(), main)
-        .assertSuccessWithOutputLines("Sub::foo", "Sub::boo", "true");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
deleted file mode 100644
index 327878a..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInMultifileClassTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInMultifileClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path multifileLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String multifileLibFolder = PKG_PREFIX + "/multifileclass_lib";
-    multifileLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(
-                getKotlinFileInTest(multifileLibFolder, "signed"),
-                getKotlinFileInTest(multifileLibFolder, "unsigned"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInMultifileClass_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(multifileLibJar)
-            // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
-            .addKeepRules("-keep class **.UtilKt")
-            .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
-    final String signedClassName = pkg + ".multifileclass_lib.UtilKt__SignedKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject util = inspector.clazz(utilClassName);
-      assertThat(util, isPresent());
-      assertThat(util, not(isRenamed()));
-      MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
-      assertThat(commaJoinOfInt, isPresent());
-      assertThat(commaJoinOfInt, not(isRenamed()));
-      MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
-      assertThat(joinOfInt, not(isPresent()));
-      // API entry is kept, hence the presence of Metadata.
-      AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
-      assertThat(annotationSubject, isPresent());
-      // TODO(b/70169921): need further inspection.
-
-      ClassSubject signed = inspector.clazz(signedClassName);
-      assertThat(signed, isRenamed());
-      commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
-      assertThat(commaJoinOfInt, isPresent());
-      assertThat(commaJoinOfInt, not(isRenamed()));
-      joinOfInt = signed.uniqueMethodWithName("joinOfInt");
-      assertThat(joinOfInt, isRenamed());
-      // API entry is kept, hence the presence of Metadata.
-      annotationSubject = util.annotation(METADATA_TYPE);
-      assertThat(annotationSubject, isPresent());
-      // TODO(b/70169921): need further inspection.
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/multifileclass_app";
-    ProcessResult kotlinTestCompileResult =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/143687784): update to just .compile() once fixed.
-            .compileRaw();
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
-  }
-
-  @Test
-  public void testMetadataInMultifileClass_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(multifileLibJar)
-            // Keep UtilKt#comma*Join*().
-            .addKeepRules("-keep class **.UtilKt")
-            .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
-            // Keep yet rename joinOf*(String).
-            .addKeepRules(
-                "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
-    final String signedClassName = pkg + ".multifileclass_lib.UtilKt__SignedKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject util = inspector.clazz(utilClassName);
-      assertThat(util, isPresent());
-      assertThat(util, not(isRenamed()));
-      MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
-      assertThat(commaJoinOfInt, isPresent());
-      assertThat(commaJoinOfInt, not(isRenamed()));
-      MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
-      assertThat(joinOfInt, isPresent());
-      assertThat(joinOfInt, isRenamed());
-      // API entry is kept, hence the presence of Metadata.
-      AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
-      assertThat(annotationSubject, isPresent());
-      // TODO(b/70169921): need further inspection.
-
-      ClassSubject signed = inspector.clazz(signedClassName);
-      assertThat(signed, isRenamed());
-      commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
-      assertThat(commaJoinOfInt, isPresent());
-      assertThat(commaJoinOfInt, not(isRenamed()));
-      joinOfInt = signed.uniqueMethodWithName("joinOfInt");
-      assertThat(joinOfInt, isRenamed());
-      // API entry is kept, hence the presence of Metadata.
-      annotationSubject = util.annotation(METADATA_TYPE);
-      assertThat(annotationSubject, isPresent());
-      // TODO(b/70169921): need further inspection.
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/multifileclass_app";
-    ProcessResult kotlinTestCompileResult =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            // TODO(b/143687784): update to just .compile() once fixed.
-            .compileRaw();
-    // TODO(b/70169921): should be able to compile!
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
deleted file mode 100644
index d75ec09..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInParameterTypeTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInParameterTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path parameterTypeLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib";
-    parameterTypeLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInParameterType_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(parameterTypeLibJar)
-            // Keep non-private members of Impl
-            .addKeepRules("-keep public class **.Impl { !private *; }")
-            // Keep Itf, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Itf")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String itfClassName = pkg + ".parametertype_lib.Itf";
-    final String implClassName = pkg + ".parametertype_lib.Impl";
-    compileResult.inspect(inspector -> {
-      ClassSubject itf = inspector.clazz(itfClassName);
-      assertThat(itf, isRenamed());
-
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Itf")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-      List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
-      assertTrue(parameterTypes.stream().anyMatch(
-          parameterType -> parameterType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/parametertype_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".parametertype_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
deleted file mode 100644
index 556780c..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInPropertyTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInPropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path propertyTypeLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String propertyTypeLibFolder = PKG_PREFIX + "/fragile_property_lib";
-    propertyTypeLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInProperty_getterOnly() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(propertyTypeLibJar)
-            // Keep property getters
-            .addKeepRules("-keep class **.Person { <init>(...); }")
-            .addKeepRules("-keepclassmembers class **.Person { *** get*(); }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String personClassName = pkg + ".fragile_property_lib.Person";
-    compileResult.inspect(inspector -> {
-      ClassSubject person = inspector.clazz(personClassName);
-      assertThat(person, isPresent());
-      assertThat(person, not(isRenamed()));
-
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = person.getKmClass();
-      assertThat(kmClass, isPresent());
-
-      KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
-      assertThat(name, isPresent());
-      assertThat(name, not(isExtensionProperty()));
-      assertNotNull(name.fieldSignature());
-      assertNotNull(name.getterSignature());
-      assertNull(name.setterSignature());
-
-      KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
-      assertThat(familyName, isPresent());
-      assertThat(familyName, not(isExtensionProperty()));
-      // No backing field for property `familyName`
-      assertNull(familyName.fieldSignature());
-      assertNotNull(familyName.getterSignature());
-      // No setter for property `familyName`
-      assertNull(familyName.setterSignature());
-
-      KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
-      assertThat(age, isPresent());
-      assertThat(age, not(isExtensionProperty()));
-      assertNotNull(age.fieldSignature());
-      assertNotNull(age.getterSignature());
-      assertNull(name.setterSignature());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/fragile_property_only_getter";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "getter_user"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".fragile_property_only_getter.Getter_userKt")
-        .assertSuccessWithOutputLines("true", "false", "Hey");
-  }
-
-  @Test
-  public void testMetadataInProperty_setterOnly() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(propertyTypeLibJar)
-            // Keep property setters (and users)
-            .addKeepRules("-keep class **.Person { <init>(...); }")
-            .addKeepRules("-keepclassmembers class **.Person { void set*(...); }")
-            .addKeepRules("-keepclassmembers class **.Person { void aging(); }")
-            .addKeepRules("-keepclassmembers class **.Person { void change*(...); }")
-            // Keep LibKt extension methods
-            .addKeepRules("-keep class **.LibKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String personClassName = pkg + ".fragile_property_lib.Person";
-    compileResult.inspect(inspector -> {
-      ClassSubject person = inspector.clazz(personClassName);
-      assertThat(person, isPresent());
-      assertThat(person, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = person.getKmClass();
-      assertThat(kmClass, isPresent());
-
-      KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
-      assertThat(name, isPresent());
-      assertThat(name, not(isExtensionProperty()));
-      assertNull(name.fieldSignature());
-      assertNull(name.getterSignature());
-      assertNotNull(name.setterSignature());
-
-      KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
-      assertThat(familyName, not(isPresent()));
-
-      KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
-      assertThat(age, isPresent());
-      assertThat(age, not(isExtensionProperty()));
-      assertNotNull(age.fieldSignature());
-      assertNull(age.getterSignature());
-      assertNotNull(age.setterSignature());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/fragile_property_only_setter";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "setter_user"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".fragile_property_only_setter.Setter_userKt")
-        .assertSuccessWithOutputLines();
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
deleted file mode 100644
index 78a1514..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInPropertyTypeTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInPropertyTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path propertyTypeLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib";
-    propertyTypeLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInProperty_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(propertyTypeLibJar)
-            // Keep non-private members of Impl
-            .addKeepRules("-keep public class **.Impl { !private *; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String itfClassName = pkg + ".propertytype_lib.Itf";
-    final String implClassName = pkg + ".propertytype_lib.Impl";
-    compileResult.inspect(inspector -> {
-      ClassSubject itf = inspector.clazz(itfClassName);
-      assertThat(itf, isRenamed());
-
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Itf")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-      List<ClassSubject> propertyReturnTypes = kmClass.getReturnTypesInProperties();
-      assertTrue(propertyReturnTypes.stream().anyMatch(
-          propertyType -> propertyType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/propertytype_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".propertytype_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::8");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
deleted file mode 100644
index 4f24442..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInReturnTypeTest extends KotlinMetadataTestBase {
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInReturnTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path returnTypeLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib";
-    returnTypeLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInReturnType_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(returnTypeLibJar)
-            // Keep non-private members of Impl
-            .addKeepRules("-keep public class **.Impl { !private *; }")
-            // Keep Itf, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.Itf")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String itfClassName = pkg + ".returntype_lib.Itf";
-    final String implClassName = pkg + ".returntype_lib.Impl";
-    compileResult.inspect(inspector -> {
-      ClassSubject itf = inspector.clazz(itfClassName);
-      assertThat(itf, isRenamed());
-
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Itf")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-      List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
-      assertTrue(functionReturnTypes.stream().anyMatch(
-          returnType -> returnType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/returntype_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".returntype_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
-  }
-}
-
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
deleted file mode 100644
index a2d4fa1..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
-import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInSealedClassTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInSealedClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path sealedLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String sealedLibFolder = PKG_PREFIX + "/sealed_lib";
-    sealedLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(getKotlinFileInTest(sealedLibFolder, "lib"))
-            .compile();
-  }
-
-  @Test
-  public void testMetadataInSealedClass_valid() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(sealedLibJar)
-            // Keep the Expr class
-            .addKeepRules("-keep class **.Expr")
-            // Keep the extension function
-            .addKeepRules("-keep class **.LibKt { <methods>; }")
-            // Keep the factory object and utils
-            .addKeepRules("-keep class **.ExprFactory { *; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String numClassName = pkg + ".sealed_lib.Num";
-    final String exprClassName = pkg + ".sealed_lib.Expr";
-    final String libClassName = pkg + ".sealed_lib.LibKt";
-    compileResult.inspect(inspector -> {
-      ClassSubject num = inspector.clazz(numClassName);
-      assertThat(num, isRenamed());
-
-      ClassSubject expr = inspector.clazz(exprClassName);
-      assertThat(expr, isPresent());
-      assertThat(expr, not(isRenamed()));
-
-      KmClassSubject kmClass = expr.getKmClass();
-      assertThat(kmClass, isPresent());
-
-      kmClass.getSealedSubclassDescriptors().forEach(sealedSubclassDescriptor -> {
-        ClassSubject sealedSubclass =
-            inspector.clazz(descriptorToJavaType(sealedSubclassDescriptor));
-        assertThat(sealedSubclass, isRenamed());
-        assertEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
-      });
-
-      ClassSubject libKt = inspector.clazz(libClassName);
-      assertThat(expr, isPresent());
-      assertThat(expr, not(isRenamed()));
-
-      KmPackageSubject kmPackage = libKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
-      assertThat(eval, isPresent());
-      assertThat(eval, isExtensionFunction());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/sealed_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "valid"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".sealed_app.ValidKt")
-        .assertSuccessWithOutputLines("6");
-  }
-
-  @Test
-  public void testMetadataInSealedClass_invalid() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(sealedLibJar)
-            // Keep the Expr class
-            .addKeepRules("-keep class **.Expr")
-            // Keep the extension function
-            .addKeepRules("-keep class **.LibKt { <methods>; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String exprClassName = pkg + ".sealed_lib.Expr";
-    final String numClassName = pkg + ".sealed_lib.Num";
-    final String libClassName = pkg + ".sealed_lib.LibKt";
-    compileResult.inspect(inspector -> {
-      // Without any specific keep rule and no instantiation point, it's not necessary to keep
-      // sub classes of Expr.
-      ClassSubject num = inspector.clazz(numClassName);
-      assertThat(num, not(isPresent()));
-
-      ClassSubject expr = inspector.clazz(exprClassName);
-      assertThat(expr, isPresent());
-      assertThat(expr, not(isRenamed()));
-
-      KmClassSubject kmClass = expr.getKmClass();
-      assertThat(kmClass, isPresent());
-
-      assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
-
-      ClassSubject libKt = inspector.clazz(libClassName);
-      assertThat(expr, isPresent());
-      assertThat(expr, not(isRenamed()));
-
-      KmPackageSubject kmPackage = libKt.getKmPackage();
-      assertThat(kmPackage, isPresent());
-
-      KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
-      assertThat(eval, isPresent());
-      assertThat(eval, isExtensionFunction());
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/sealed_app";
-    ProcessResult kotlinTestCompileResult =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "invalid"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compileRaw();
-
-    assertNotEquals(0, kotlinTestCompileResult.exitCode);
-    assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
-    assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
deleted file mode 100644
index 722004a..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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.kotlin.metadata;
-
-import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.KmClassSubject;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MetadataRenameInSuperTypeTest extends KotlinMetadataTestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0} target: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
-  }
-
-  public MetadataRenameInSuperTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion) {
-    super(targetVersion);
-    this.parameters = parameters;
-  }
-
-  private static Path superTypeLibJar;
-
-  @BeforeClass
-  public static void createLibJar() throws Exception {
-    String superTypeLibFolder = PKG_PREFIX + "/supertype_lib";
-    superTypeLibJar =
-        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addSourceFiles(
-                getKotlinFileInTest(superTypeLibFolder, "impl"),
-                getKotlinFileInTest(superTypeLibFolder + "/internal", "itf"))
-            .compile();
-  }
-
-  @Test
-  public void b143687784_merged() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(superTypeLibJar)
-            // Keep non-private members except for ones in `internal` definitions.
-            .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String itfClassName = pkg + ".supertype_lib.internal.Itf";
-    final String implClassName = pkg + ".supertype_lib.Impl";
-    compileResult.inspect(inspector -> {
-      assertThat(inspector.clazz(itfClassName), not(isPresent()));
-
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("internal")));
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Itf")));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/supertype_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
-  }
-
-  @Test
-  public void b143687784_renamed() throws Exception {
-    R8TestCompileResult compileResult =
-        testForR8(parameters.getBackend())
-            .addProgramFiles(superTypeLibJar)
-            // Keep non-private members except for ones in `internal` definitions.
-            .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
-            // Keep `internal` definitions, but allow minification.
-            .addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
-            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
-            .compile();
-    String pkg = getClass().getPackage().getName();
-    final String itfClassName = pkg + ".supertype_lib.internal.Itf";
-    final String implClassName = pkg + ".supertype_lib.Impl";
-    compileResult.inspect(inspector -> {
-      ClassSubject itf = inspector.clazz(itfClassName);
-      assertThat(itf, isRenamed());
-
-      ClassSubject impl = inspector.clazz(implClassName);
-      assertThat(impl, isPresent());
-      assertThat(impl, not(isRenamed()));
-      // API entry is kept, hence the presence of Metadata.
-      KmClassSubject kmClass = impl.getKmClass();
-      assertThat(kmClass, isPresent());
-      List<ClassSubject> superTypes = kmClass.getSuperTypes();
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("internal")));
-      assertTrue(superTypes.stream().noneMatch(
-          supertype -> supertype.getFinalDescriptor().contains("Itf")));
-      assertTrue(superTypes.stream().anyMatch(
-          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
-    });
-
-    Path libJar = compileResult.writeToZip();
-
-    String appFolder = PKG_PREFIX + "/supertype_app";
-    Path output =
-        kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(libJar)
-            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
-            .setOutputPath(temp.newFolder().toPath())
-            .compile();
-
-    testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
-        .addClasspath(output)
-        .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
-        .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
new file mode 100644
index 0000000..d03fcd1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -0,0 +1,199 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInClasspathTypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInClasspathTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>();
+  private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String baseLibFolder = PKG_PREFIX + "/classpath_lib_base";
+    String extLibFolder = PKG_PREFIX + "/classpath_lib_ext";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path baseLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(baseLibFolder, "itf"))
+              .compile();
+      Path extLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addClasspathFiles(baseLibJar)
+              .addSourceFiles(getKotlinFileInTest(extLibFolder, "impl"))
+              .compile();
+      baseLibJarMap.put(targetVersion, baseLibJar);
+      extLibJarMap.put(targetVersion, extLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInClasspathType_merged() throws Exception {
+    Path baseLibJar = baseLibJarMap.get(targetVersion);
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(baseLibJar)
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the Extra class and its interface (which has the method).
+            .addKeepRules("-keep class **.Extra")
+            // Keep the ImplKt extension method which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.ImplKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(baseLibJar, libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo");
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String implClassName = PKG + ".classpath_lib_ext.Impl";
+    String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
+    String extraClassName = PKG + ".classpath_lib_ext.Extra";
+
+    assertThat(inspector.clazz(implClassName), not(isPresent()));
+
+    ClassSubject implKt = inspector.clazz(implKtClassName);
+    assertThat(implKt, isPresent());
+    assertThat(implKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = implKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
+    assertThat(kmFunction, isPresent());
+
+    ClassSubject extra = inspector.clazz(extraClassName);
+    assertThat(extra, isPresent());
+    assertThat(extra, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = extra.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Impl")));
+    // Can't build ClassSubject with Itf in classpath. Instead, check if the reference to Itf is
+    // not altered via descriptors.
+    List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
+    assertTrue(superTypeDescriptors.stream().noneMatch(supertype -> supertype.contains("Impl")));
+    assertTrue(superTypeDescriptors.stream().anyMatch(supertype -> supertype.contains("Itf")));
+  }
+
+  @Test
+  public void testMetadataInClasspathType_renamed() throws Exception {
+    Path baseLibJar = baseLibJarMap.get(targetVersion);
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(baseLibJar)
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the Extra class and its interface (which has the method).
+            .addKeepRules("-keep class **.Extra")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Impl")
+            // Keep the ImplKt extension method which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.ImplKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(baseLibJar, libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo");
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String implClassName = PKG + ".classpath_lib_ext.Impl";
+    String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
+    String extraClassName = PKG + ".classpath_lib_ext.Extra";
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isRenamed());
+
+    ClassSubject implKt = inspector.clazz(implKtClassName);
+    assertThat(implKt, isPresent());
+    assertThat(implKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = implKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
+    assertThat(kmFunction, isExtensionFunction());
+
+    ClassSubject extra = inspector.clazz(extraClassName);
+    assertThat(extra, isPresent());
+    assertThat(extra, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = extra.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Impl")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(impl.getFinalDescriptor())));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
new file mode 100644
index 0000000..5059a1e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInCompanionTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInCompanionTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static Map<KotlinTargetVersion, Path> companionLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String companionLibFolder = PKG_PREFIX + "/companion_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path companionLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(companionLibFolder, "lib"))
+              .compile();
+      companionLibJarMap.put(targetVersion, companionLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInCompanion_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(companionLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep getters for B$Companion.(singleton|foo) which will be referenced at the app.
+            .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
+            // No rule for Super, but will be kept and renamed.
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+
+    ProcessResult kotlinTestCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode);
+    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: singleton"));
+    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+  }
+
+  private void inspect(CodeInspector inspector) {
+    final String superClassName = PKG + ".companion_lib.Super";
+    final String bClassName = PKG + ".companion_lib.B";
+    final String companionClassName = PKG + ".companion_lib.B$Companion";
+
+    ClassSubject sup = inspector.clazz(superClassName);
+    assertThat(sup, isRenamed());
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+    // Bridge for the property in the companion that needs a backing field.
+    MethodSubject singletonBridge = impl.uniqueMethodWithName("access$getSingleton$cp");
+    assertThat(singletonBridge, isRenamed());
+
+    // For B$Companion.foo, no backing field needed, hence no bridge.
+    MethodSubject fooBridge = impl.uniqueMethodWithName("access$getFoo$cp");
+    assertThat(fooBridge, not(isPresent()));
+
+    ClassSubject companion = inspector.clazz(companionClassName);
+    assertThat(companion, isRenamed());
+
+    MethodSubject singletonGetter = companion.uniqueMethodWithName("getSingleton");
+    assertThat(singletonGetter, isPresent());
+
+    MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo");
+    assertThat(fooGetter, isPresent());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
new file mode 100644
index 0000000..917ede7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -0,0 +1,185 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInExtensionFunctionTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInExtensionFunctionTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String extLibFolder = PKG_PREFIX + "/extension_function_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path extLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
+              .compile();
+      extLibJarMap.put(targetVersion, extLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInExtensionFunction_merged() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep the BKt extension function which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String superClassName = PKG + ".extension_function_lib.Super";
+    String bClassName = PKG + ".extension_function_lib.B";
+    String bKtClassName = PKG + ".extension_function_lib.BKt";
+
+    assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
+    assertThat(kmFunction, isExtensionFunction());
+  }
+
+  @Test
+  public void testMetadataInExtensionFunction_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Super")
+            // Keep the BKt extension function which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String superClassName = PKG + ".extension_function_lib.Super";
+    String bClassName = PKG + ".extension_function_lib.B";
+    String bKtClassName = PKG + ".extension_function_lib.BKt";
+
+    ClassSubject sup = inspector.clazz(superClassName);
+    assertThat(sup, isPresent());
+    assertThat(sup, isRenamed());
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
+    assertThat(kmFunction, isExtensionFunction());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
new file mode 100644
index 0000000..5fb5150
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInExtensionPropertyTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInExtensionPropertyTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String extLibFolder = PKG_PREFIX + "/extension_property_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path extLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
+              .compile();
+      extLibJarMap.put(targetVersion, extLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInExtensionProperty_merged() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep the BKt extension property which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String superClassName = PKG + ".extension_property_lib.Super";
+    String bClassName = PKG + ".extension_property_lib.B";
+    String bKtClassName = PKG + ".extension_property_lib.BKt";
+
+    assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+    KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName("doStuff");
+    assertThat(kmFunction, isPresent());
+    assertThat(kmFunction, not(isExtensionFunction()));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
+    assertThat(kmProperty, isExtensionProperty());
+    assertNotNull(kmProperty.getterSignature());
+  }
+
+  @Test
+  public void testMetadataInExtensionProperty_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(extLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Super")
+            // Keep the BKt extension property which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String superClassName = PKG + ".extension_property_lib.Super";
+    String bClassName = PKG + ".extension_property_lib.B";
+    String bKtClassName = PKG + ".extension_property_lib.BKt";
+
+    ClassSubject sup = inspector.clazz(superClassName);
+    assertThat(sup, isRenamed());
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI");
+    assertThat(kmProperty, isExtensionProperty());
+    assertNotNull(kmProperty.getterSignature());
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
new file mode 100644
index 0000000..f6649b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInFunctionTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInFunctionTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> funLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String funLibFolder = PKG_PREFIX + "/function_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path funLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(funLibFolder, "B"))
+              .compile();
+      funLibJarMap.put(targetVersion, funLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInFunction_merged() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(funLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep the BKt method, which will be called from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String superClassName = PKG + ".function_lib.Super";
+    String bClassName = PKG + ".function_lib.B";
+    String bKtClassName = PKG + ".function_lib.BKt";
+
+    assertThat(inspector.clazz(superClassName), not(isPresent()));
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
+    assertThat(kmFunction, isPresent());
+    assertThat(kmFunction, not(isExtensionFunction()));
+  }
+
+  @Test
+  public void testMetadataInFunction_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(funLibJarMap.get(targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Super")
+            // Keep the BKt method, which will be called from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
+        .assertSuccessWithOutputLines("do stuff", "do stuff");
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String superClassName = PKG + ".function_lib.Super";
+    String bClassName = PKG + ".function_lib.B";
+    String bKtClassName = PKG + ".function_lib.BKt";
+
+    ClassSubject sup = inspector.clazz(superClassName);
+    assertThat(sup, isRenamed());
+
+    ClassSubject impl = inspector.clazz(bClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Super")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+
+    ClassSubject bKt = inspector.clazz(bKtClassName);
+    assertThat(bKt, isPresent());
+    assertThat(bKt, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = bKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun");
+    assertThat(kmFunction, isPresent());
+    assertThat(kmFunction, not(isExtensionFunction()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
new file mode 100644
index 0000000..ba6b08f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInLibraryTypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInLibraryTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>();
+  private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>();
+  private static final Map<KotlinTargetVersion, Path> appJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String baseLibFolder = PKG_PREFIX + "/libtype_lib_base";
+    String extLibFolder = PKG_PREFIX + "/libtype_lib_ext";
+    String appFolder = PKG_PREFIX + "/libtype_app";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path baseLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(baseLibFolder, "base"))
+              .compile();
+      Path extLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addClasspathFiles(baseLibJar)
+              .addSourceFiles(getKotlinFileInTest(extLibFolder, "ext"))
+              .compile();
+      Path appJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addClasspathFiles(baseLibJar)
+              .addClasspathFiles(extLibJar)
+              .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+              .compile();
+      baseLibJarMap.put(targetVersion, baseLibJar);
+      extLibJarMap.put(targetVersion, extLibJar);
+      appJarMap.put(targetVersion, appJar);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    String main = PKG + ".libtype_app.MainKt";
+    Path out =
+        testForR8(parameters.getBackend())
+            // Intentionally not providing basLibJar as lib file nor classpath file.
+            .addProgramFiles(extLibJarMap.get(targetVersion), appJarMap.get(targetVersion))
+            // Keep Ext extension method which requires metadata to be called with Kotlin syntax
+            // from other kotlin code.
+            .addKeepRules("-keep class **.ExtKt { <methods>; }")
+            // Keep the main entry.
+            .addKeepMainRule(main)
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .allowDiagnosticWarningMessages()
+            // -dontoptimize so that basic code structure is kept.
+            .noOptimization()
+            .compile()
+            .inspect(this::inspect)
+            .assertAllWarningMessagesMatch(
+                anyOf(
+                    equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+                    equalTo("Resource 'META-INF/main.kotlin_module' already exists.")))
+            .writeToZip();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion))
+        .addClasspath(out)
+        .run(parameters.getRuntime(), main)
+        .assertSuccessWithOutputLines("Sub::foo", "Sub::boo", "true");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    String extClassName = PKG + ".libtype_lib_ext.ExtKt";
+    ClassSubject ext = inspector.clazz(extClassName);
+    assertThat(ext, isPresent());
+    assertThat(ext, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = ext.getKmPackage();
+    assertThat(kmPackage, isPresent());
+    // Type appearance of library type, Base, should be kept, even if it's not provided.
+    // Note that the resulting ClassSubject for Base is an absent one as we don't provide it, and
+    // thus we can't use `getReturnTypesInFunctions`, which filters out absent class subject.
+    assertTrue(kmPackage.getReturnTypeDescriptorsInFunctions().stream().anyMatch(
+        returnType -> returnType.contains("Base")));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
new file mode 100644
index 0000000..2dc57bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -0,0 +1,177 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInMultifileClassTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInMultifileClassTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> multifileLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String multifileLibFolder = PKG_PREFIX + "/multifileclass_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path multifileLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(
+                  getKotlinFileInTest(multifileLibFolder, "signed"),
+                  getKotlinFileInTest(multifileLibFolder, "unsigned"))
+              .compile();
+      multifileLibJarMap.put(targetVersion, multifileLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInMultifileClass_merged() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(multifileLibJarMap.get(targetVersion))
+            // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
+            .addKeepRules("-keep class **.UtilKt")
+            .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    ProcessResult kotlinTestCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode);
+    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String utilClassName = PKG + ".multifileclass_lib.UtilKt";
+    String signedClassName = PKG + ".multifileclass_lib.UtilKt__SignedKt";
+
+    ClassSubject util = inspector.clazz(utilClassName);
+    assertThat(util, isPresent());
+    assertThat(util, not(isRenamed()));
+    MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
+    assertThat(commaJoinOfInt, isPresent());
+    assertThat(commaJoinOfInt, not(isRenamed()));
+    MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
+    assertThat(joinOfInt, not(isPresent()));
+    // API entry is kept, hence the presence of Metadata.
+    AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
+    assertThat(annotationSubject, isPresent());
+    // TODO(b/70169921): need further inspection.
+
+    ClassSubject signed = inspector.clazz(signedClassName);
+    assertThat(signed, isRenamed());
+    commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
+    assertThat(commaJoinOfInt, isPresent());
+    assertThat(commaJoinOfInt, not(isRenamed()));
+    joinOfInt = signed.uniqueMethodWithName("joinOfInt");
+    assertThat(joinOfInt, isRenamed());
+    // API entry is kept, hence the presence of Metadata.
+    annotationSubject = util.annotation(METADATA_TYPE);
+    assertThat(annotationSubject, isPresent());
+    // TODO(b/70169921): need further inspection.
+  }
+
+  @Test
+  public void testMetadataInMultifileClass_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(multifileLibJarMap.get(targetVersion))
+            // Keep UtilKt#comma*Join*().
+            .addKeepRules("-keep class **.UtilKt")
+            .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
+            // Keep yet rename joinOf*(String).
+            .addKeepRules(
+                "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    ProcessResult kotlinTestCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode);
+    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String utilClassName = PKG + ".multifileclass_lib.UtilKt";
+    String signedClassName = PKG + ".multifileclass_lib.UtilKt__SignedKt";
+
+    ClassSubject util = inspector.clazz(utilClassName);
+    assertThat(util, isPresent());
+    assertThat(util, not(isRenamed()));
+    MethodSubject commaJoinOfInt = util.uniqueMethodWithName("commaSeparatedJoinOfInt");
+    assertThat(commaJoinOfInt, isPresent());
+    assertThat(commaJoinOfInt, not(isRenamed()));
+    MethodSubject joinOfInt = util.uniqueMethodWithName("joinOfInt");
+    assertThat(joinOfInt, isPresent());
+    assertThat(joinOfInt, isRenamed());
+    // API entry is kept, hence the presence of Metadata.
+    AnnotationSubject annotationSubject = util.annotation(METADATA_TYPE);
+    assertThat(annotationSubject, isPresent());
+    // TODO(b/70169921): need further inspection.
+
+    ClassSubject signed = inspector.clazz(signedClassName);
+    assertThat(signed, isRenamed());
+    commaJoinOfInt = signed.uniqueMethodWithName("commaSeparatedJoinOfInt");
+    assertThat(commaJoinOfInt, isPresent());
+    assertThat(commaJoinOfInt, not(isRenamed()));
+    joinOfInt = signed.uniqueMethodWithName("joinOfInt");
+    assertThat(joinOfInt, isRenamed());
+    // API entry is kept, hence the presence of Metadata.
+    annotationSubject = util.annotation(METADATA_TYPE);
+    assertThat(annotationSubject, isPresent());
+    // TODO(b/70169921): need further inspection.
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
new file mode 100644
index 0000000..6dcbc1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -0,0 +1,111 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInParameterTypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInParameterTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> parameterTypeLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path parameterTypeLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib"))
+              .compile();
+      parameterTypeLibJarMap.put(targetVersion, parameterTypeLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInParameterType_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(parameterTypeLibJarMap.get(targetVersion))
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            // Keep Itf, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Itf")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    String itfClassName = PKG + ".parametertype_lib.Itf";
+    String implClassName = PKG + ".parametertype_lib.Impl";
+
+    ClassSubject itf = inspector.clazz(itfClassName);
+    assertThat(itf, isRenamed());
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Itf")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+    List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
+    assertTrue(parameterTypes.stream().anyMatch(
+        parameterType -> parameterType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
new file mode 100644
index 0000000..d8b230d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInPropertyTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInPropertyTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String propertyTypeLibFolder = PKG_PREFIX + "/fragile_property_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path propertyTypeLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
+              .compile();
+      propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInProperty_getterOnly() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+            // Keep property getters
+            .addKeepRules("-keep class **.Person { <init>(...); }")
+            .addKeepRules("-keepclassmembers class **.Person { *** get*(); }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectGetterOnly)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
+        .assertSuccessWithOutputLines("true", "false", "Hey");
+  }
+
+  private void inspectGetterOnly(CodeInspector inspector) {
+    String personClassName = PKG + ".fragile_property_lib.Person";
+    ClassSubject person = inspector.clazz(personClassName);
+    assertThat(person, isPresent());
+    assertThat(person, not(isRenamed()));
+
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = person.getKmClass();
+    assertThat(kmClass, isPresent());
+
+    KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
+    assertThat(name, isPresent());
+    assertThat(name, not(isExtensionProperty()));
+    assertNotNull(name.fieldSignature());
+    assertNotNull(name.getterSignature());
+    assertNull(name.setterSignature());
+
+    KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
+    assertThat(familyName, isPresent());
+    assertThat(familyName, not(isExtensionProperty()));
+    // No backing field for property `familyName`
+    assertNull(familyName.fieldSignature());
+    assertNotNull(familyName.getterSignature());
+    // No setter for property `familyName`
+    assertNull(familyName.setterSignature());
+
+    KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
+    assertThat(age, isPresent());
+    assertThat(age, not(isExtensionProperty()));
+    assertNotNull(age.fieldSignature());
+    assertNotNull(age.getterSignature());
+    assertNull(name.setterSignature());
+  }
+
+  @Test
+  public void testMetadataInProperty_setterOnly() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+            // Keep property setters (and users)
+            .addKeepRules("-keep class **.Person { <init>(...); }")
+            .addKeepRules("-keepclassmembers class **.Person { void set*(...); }")
+            .addKeepRules("-keepclassmembers class **.Person { void aging(); }")
+            .addKeepRules("-keepclassmembers class **.Person { void change*(...); }")
+            // Keep LibKt extension methods
+            .addKeepRules("-keep class **.LibKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectSetterOnly)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt")
+        .assertSuccessWithOutputLines();
+  }
+
+  private void inspectSetterOnly(CodeInspector inspector) {
+    String personClassName = PKG + ".fragile_property_lib.Person";
+    ClassSubject person = inspector.clazz(personClassName);
+    assertThat(person, isPresent());
+    assertThat(person, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = person.getKmClass();
+    assertThat(kmClass, isPresent());
+
+    KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
+    assertThat(name, isPresent());
+    assertThat(name, not(isExtensionProperty()));
+    assertNull(name.fieldSignature());
+    assertNull(name.getterSignature());
+    assertNotNull(name.setterSignature());
+
+    KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
+    assertThat(familyName, not(isPresent()));
+
+    KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
+    assertThat(age, isPresent());
+    assertThat(age, not(isExtensionProperty()));
+    assertNotNull(age.fieldSignature());
+    assertNull(age.getterSignature());
+    assertNotNull(age.setterSignature());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
new file mode 100644
index 0000000..8aba4f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -0,0 +1,109 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInPropertyTypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInPropertyTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path propertyTypeLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
+              .compile();
+      propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInProperty_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(propertyTypeLibJarMap.get(targetVersion))
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::8");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    String itfClassName = PKG + ".propertytype_lib.Itf";
+    String implClassName = PKG + ".propertytype_lib.Impl";
+
+    ClassSubject itf = inspector.clazz(itfClassName);
+    assertThat(itf, isRenamed());
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Itf")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+    List<ClassSubject> propertyReturnTypes = kmClass.getReturnTypesInProperties();
+    assertTrue(propertyReturnTypes.stream().anyMatch(
+        propertyType -> propertyType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
new file mode 100644
index 0000000..1e33e7c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -0,0 +1,111 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInReturnTypeTest extends KotlinMetadataTestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInReturnTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> returnTypeLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path returnTypeLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib"))
+              .compile();
+      returnTypeLibJarMap.put(targetVersion, returnTypeLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInReturnType_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(returnTypeLibJarMap.get(targetVersion))
+            // Keep non-private members of Impl
+            .addKeepRules("-keep public class **.Impl { !private *; }")
+            // Keep Itf, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Itf")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    String itfClassName = PKG + ".returntype_lib.Itf";
+    String implClassName = PKG + ".returntype_lib.Impl";
+
+    ClassSubject itf = inspector.clazz(itfClassName);
+    assertThat(itf, isRenamed());
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Itf")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+    List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
+    assertTrue(functionReturnTypes.stream().anyMatch(
+        returnType -> returnType.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+  }
+}
+
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
new file mode 100644
index 0000000..6cd1034
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInSealedClassTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInSealedClassTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> sealedLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String sealedLibFolder = PKG_PREFIX + "/sealed_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path sealedLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(sealedLibFolder, "lib"))
+              .compile();
+      sealedLibJarMap.put(targetVersion, sealedLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInSealedClass_valid() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(sealedLibJarMap.get(targetVersion))
+            // Keep the Expr class
+            .addKeepRules("-keep class **.Expr")
+            // Keep the extension function
+            .addKeepRules("-keep class **.LibKt { <methods>; }")
+            // Keep the factory object and utils
+            .addKeepRules("-keep class **.ExprFactory { *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectValid)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "valid"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
+        .assertSuccessWithOutputLines("6");
+  }
+
+  private void inspectValid(CodeInspector inspector) {
+    String numClassName = PKG + ".sealed_lib.Num";
+    String exprClassName = PKG + ".sealed_lib.Expr";
+    String libClassName = PKG + ".sealed_lib.LibKt";
+
+    ClassSubject num = inspector.clazz(numClassName);
+    assertThat(num, isRenamed());
+
+    ClassSubject expr = inspector.clazz(exprClassName);
+    assertThat(expr, isPresent());
+    assertThat(expr, not(isRenamed()));
+
+    KmClassSubject kmClass = expr.getKmClass();
+    assertThat(kmClass, isPresent());
+
+    kmClass.getSealedSubclassDescriptors().forEach(sealedSubclassDescriptor -> {
+      ClassSubject sealedSubclass =
+          inspector.clazz(descriptorToJavaType(sealedSubclassDescriptor));
+      assertThat(sealedSubclass, isRenamed());
+      assertEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
+    });
+
+    ClassSubject libKt = inspector.clazz(libClassName);
+    assertThat(expr, isPresent());
+    assertThat(expr, not(isRenamed()));
+
+    KmPackageSubject kmPackage = libKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
+    assertThat(eval, isPresent());
+    assertThat(eval, isExtensionFunction());
+  }
+
+  @Test
+  public void testMetadataInSealedClass_invalid() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(sealedLibJarMap.get(targetVersion))
+            // Keep the Expr class
+            .addKeepRules("-keep class **.Expr")
+            // Keep the extension function
+            .addKeepRules("-keep class **.LibKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectInvalid)
+            .writeToZip();
+
+    ProcessResult kotlinTestCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "invalid"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compileRaw();
+
+    assertNotEquals(0, kotlinTestCompileResult.exitCode);
+    assertThat(kotlinTestCompileResult.stderr, containsString("cannot access"));
+    assertThat(kotlinTestCompileResult.stderr, containsString("private in 'Expr'"));
+  }
+
+  private void inspectInvalid(CodeInspector inspector) {
+    String exprClassName = PKG + ".sealed_lib.Expr";
+    String numClassName = PKG + ".sealed_lib.Num";
+    String libClassName = PKG + ".sealed_lib.LibKt";
+
+    // Without any specific keep rule and no instantiation point, it's not necessary to keep
+    // sub classes of Expr.
+    ClassSubject num = inspector.clazz(numClassName);
+    assertThat(num, not(isPresent()));
+
+    ClassSubject expr = inspector.clazz(exprClassName);
+    assertThat(expr, isPresent());
+    assertThat(expr, not(isRenamed()));
+
+    KmClassSubject kmClass = expr.getKmClass();
+    assertThat(kmClass, isPresent());
+
+    assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
+
+    ClassSubject libKt = inspector.clazz(libClassName);
+    assertThat(expr, isPresent());
+    assertThat(expr, not(isRenamed()));
+
+    KmPackageSubject kmPackage = libKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+
+    KmFunctionSubject eval = kmPackage.kmFunctionExtensionWithUniqueName("eval");
+    assertThat(eval, isPresent());
+    assertThat(eval, isExtensionFunction());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
new file mode 100644
index 0000000..ba59f4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -0,0 +1,157 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInSuperTypeTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInSuperTypeTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> superTypeLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String superTypeLibFolder = PKG_PREFIX + "/supertype_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path superTypeLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(
+                  getKotlinFileInTest(superTypeLibFolder, "impl"),
+                  getKotlinFileInTest(superTypeLibFolder + "/internal", "itf"))
+              .compile();
+      superTypeLibJarMap.put(targetVersion, superTypeLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInSupertype_merged() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(superTypeLibJarMap.get(targetVersion))
+            // Keep non-private members except for ones in `internal` definitions.
+            .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectMerged)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+  }
+
+  private void inspectMerged(CodeInspector inspector) {
+    String itfClassName = PKG + ".supertype_lib.internal.Itf";
+    String implClassName = PKG + ".supertype_lib.Impl";
+
+    assertThat(inspector.clazz(itfClassName), not(isPresent()));
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("internal")));
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Itf")));
+  }
+
+  @Test
+  public void testMetadataInSupertype_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(superTypeLibJarMap.get(targetVersion))
+            // Keep non-private members except for ones in `internal` definitions.
+            .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
+            // Keep `internal` definitions, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspectRenamed)
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
+        .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+  }
+
+  private void inspectRenamed(CodeInspector inspector) {
+    String itfClassName = PKG + ".supertype_lib.internal.Itf";
+    String implClassName = PKG + ".supertype_lib.Impl";
+
+    ClassSubject itf = inspector.clazz(itfClassName);
+    assertThat(itf, isRenamed());
+
+    ClassSubject impl = inspector.clazz(implClassName);
+    assertThat(impl, isPresent());
+    assertThat(impl, not(isRenamed()));
+    // API entry is kept, hence the presence of Metadata.
+    KmClassSubject kmClass = impl.getKmClass();
+    assertThat(kmClass, isPresent());
+    List<ClassSubject> superTypes = kmClass.getSuperTypes();
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("internal")));
+    assertTrue(superTypes.stream().noneMatch(
+        supertype -> supertype.getFinalDescriptor().contains("Itf")));
+    assertTrue(superTypes.stream().anyMatch(
+        supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
new file mode 100644
index 0000000..8b9f276
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInTypeAliasTest extends KotlinMetadataTestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteInTypeAliasTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final Map<KotlinTargetVersion, Path> typeAliasLibJarMap = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String typeAliasLibFolder = PKG_PREFIX + "/typealias_lib";
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path typeAliasLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib"))
+              .compile();
+      typeAliasLibJarMap.put(targetVersion, typeAliasLibJar);
+    }
+  }
+
+  @Test
+  public void testMetadataInTypeAlias_renamed() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(typeAliasLibJarMap.get(targetVersion))
+            // Keep non-private members of Impl
+            .addKeepRules("-keep class **.Impl { !private *; }")
+            // Keep Itf, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Itf")
+            // Keep LibKt that contains the type-aliases and utils.
+            .addKeepRules("-keep class **.LibKt { *; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+
+    ProcessResult kotlinTestCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            // TODO(b/70169921): update to just .compile() once fixed.
+            .compileRaw();
+    // TODO(b/70169921): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode);
+    assertThat(
+        kotlinTestCompileResult.stderr,
+        containsString(
+            "type mismatch: inferred type is ProgramClass but API /* = Itf */ was expected"));
+  }
+
+  private void inspect(CodeInspector inspector) {
+    String itfClassName = PKG + ".typealias_lib.Itf";
+    String libKtClassName = PKG + ".typealias_lib.LibKt";
+
+    ClassSubject itf = inspector.clazz(itfClassName);
+    assertThat(itf, isRenamed());
+
+    ClassSubject libKt = inspector.clazz(libKtClassName);
+    assertThat(libKt, isPresent());
+    assertThat(libKt, not(isRenamed()));
+
+    MethodSubject seq = libKt.uniqueMethodWithName("seq");
+    assertThat(seq, isPresent());
+    assertThat(seq, not(isRenamed()));
+
+    // API entry is kept, hence the presence of Metadata.
+    KmPackageSubject kmPackage = libKt.getKmPackage();
+    assertThat(kmPackage, isPresent());
+    // TODO(b/70169921): need further inspection: many kinds of type appearances in typealias.
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 35dd597..f93179f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -51,9 +52,11 @@
             .addKeepMainRule(mainClassName)
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .addKeepRules("-keep class kotlin.Metadata")
-            // TODO(b/145090972): Should never need to exit gracefully during testing.
-            .allowClassInlinerGracefulExit()
+            .allowDiagnosticWarningMessages()
             .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
             .run(parameters.getRuntime(), mainClassName);
     CodeInspector inspector = result.inspector();
     ClassSubject clazz = inspector.clazz(mainClassName);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
new file mode 100644
index 0000000..f0ddbf7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_app/main.kt
@@ -0,0 +1,25 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata.typealias_app
+
+import com.android.tools.r8.kotlin.metadata.typealias_lib.API
+import com.android.tools.r8.kotlin.metadata.typealias_lib.Impl
+import com.android.tools.r8.kotlin.metadata.typealias_lib.seq
+
+class ProgramClass : Impl() {
+  override fun foo(): API {
+    super.foo()
+    println("Program::foo")
+    return this
+  }
+}
+
+fun main() {
+  val instance = ProgramClass()
+  val l = seq(instance)
+  for (api in l) {
+    println(api == api.foo())
+    println(api.hey())
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt
new file mode 100644
index 0000000..82ba761
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typealias_lib/lib.kt
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin.metadata.typealias_lib
+
+// TypeAlias {
+//   name: "Jude"
+//   underlyingType {
+//     classifier: "kotlin/Long"
+//   }
+// }
+typealias Jude = Long
+
+interface Itf {
+  fun foo() : Itf
+  fun hey() : Jude
+}
+
+// TypeAlias {
+//   name: "API"
+//   underlyingType {
+//     classifier: ".../Itf"
+//   }
+// }
+typealias API = Itf
+// TypeAlias {
+//   typeParameters { KmTypeParameter { name = "T" ... } }
+//   name: "myAliasedList"
+//   underlyingType {
+//     classifier: "kotlin/Array"
+//   }
+//   expandedType == underlyingType
+// }
+typealias myAliasedArray<T> = Array<T>
+// TypeAlias {
+//   underlyingType {
+//     classifier: ".../myAliasedArray"
+//     arguments {
+//       KmTypeProjection { ... type = ".../API" }
+//     }
+//   }
+//   expandedType {
+//     classifier: "kotlin/Array"
+//     arguments {
+//       KmTypeProjection { ... type = ".../Itf" }
+//     }
+//   }
+// }
+typealias APIs = myAliasedArray<API>
+
+open class Impl : API {
+  override fun foo() : API {
+    println("Impl::foo")
+    return this
+  }
+
+  override fun hey(): Jude {
+    return 42L
+  }
+}
+
+fun seq(vararg itfs : Itf) : APIs {
+  return arrayOf(*itfs)
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 3806c05..8bc0e79 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.withNativeFileSeparators;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.GenerateMainDexList;
 import com.android.tools.r8.GenerateMainDexListCommand;
@@ -328,12 +329,14 @@
         .addKeepRules("-keepattributes *Annotation*")
         .addMainDexRuleFiles(mainDexRules)
         .addOptionsModification(optionsConsumer)
+        .allowDiagnosticWarningMessages()
         .assumeAllMethodsMayHaveSideEffects()
         .setMinApi(minSdk)
         .noMinification()
         .noTreeShaking()
         .setMainDexListConsumer(ToolHelper.consumeString(r8MainDexListOutput::set))
         .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .writeToZip(out);
 
     List<String> r8MainDexList =
diff --git a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
index 3778392..bf33b1c 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
@@ -48,6 +48,7 @@
         .addKeepMainRule(mainClass)
         // Include explicit main dex entry for class Static.
         .addMainDexListClasses(Static.class)
+        .allowDiagnosticWarningMessages()
         .compile()
         .inspect(this::classStaticGone)
         .assertOnlyWarnings()
@@ -66,6 +67,7 @@
         .addMainDexListClasses(Main.class, Static.class)
         // Include main dex rule for class Static2.
         .addMainDexClassRules(Static2.class)
+        .allowDiagnosticWarningMessages()
         .compile()
         .inspect(this::classStaticGone)
         .assertOnlyWarnings()
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index d56ae8e..7e15524 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.naming;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -33,7 +34,7 @@
   @Parameterized.Parameters(name = "{0} target: {1} minify: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(),
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
         KotlinTargetVersion.values(),
         BooleanUtils.values());
   }
@@ -52,8 +53,12 @@
             .addProgramFiles(getKotlinJarFile(FOLDER))
             .addProgramFiles(getJavaJarFile(FOLDER))
             .addKeepMainRule(MAIN_CLASS_NAME)
+            .allowDiagnosticWarningMessages()
             .minification(minify)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
             .run(parameters.getRuntime(), MAIN_CLASS_NAME)
             .inspector();
     ClassSubject enumClass = inspector.clazz(ENUM_CLASS_NAME);
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index e9383ef..1c7c5bc 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 import static com.android.tools.r8.utils.DescriptorUtils.isValidJavaType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -46,6 +47,7 @@
   private final String appFileName;
   private final List<String> keepRulesFiles;
   private final BiConsumer<TestParameters, CodeInspector> inspection;
+  private final String test;
 
   public IdentifierMinifierTest(
       TestParameters parameters,
@@ -56,18 +58,23 @@
     this.appFileName = ToolHelper.EXAMPLES_BUILD_DIR + test + FileUtils.JAR_EXTENSION;
     this.keepRulesFiles = keepRulesFiles;
     this.inspection = inspection;
+    this.test = test;
   }
 
   @Test
   public void identiferMinifierTest() throws Exception {
+    boolean hasWarning =
+        test.equals("identifiernamestring") && keepRulesFiles.get(0).endsWith("keep-rules-2.txt");
     CodeInspector codeInspector =
         testForR8(parameters.getBackend())
             .addProgramFiles(Paths.get(appFileName))
             .addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
-            .allowUnusedProguardConfigurationRules()
+            .allowDiagnosticWarningMessages(hasWarning)
             .enableProguardTestOptions()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .compile()
+            .assertAllWarningMessagesMatch(
+                containsString("Cannot determine what identifier string flows to"))
             .inspector();
     inspection.accept(parameters, codeInspector);
   }
@@ -95,7 +102,7 @@
     Collection<Object[]> parameters = NamingTestBase.createTests(tests, inspections);
 
     List<Object[]> parametersWithBackend = new ArrayList<>();
-    for (TestParameters testParameter : getTestParameters().withAllRuntimes().build()) {
+    for (TestParameters testParameter : getTestParameters().withAllRuntimesAndApiLevels().build()) {
       for (Object[] row : parameters) {
         Object[] newRow = new Object[row.length + 1];
         newRow[0] = testParameter;
@@ -315,5 +322,4 @@
             });
     return result;
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index 2b06482..e9ce1d8 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -3,11 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.code.AputObject;
 import com.android.tools.r8.code.Const4;
 import com.android.tools.r8.code.ConstClass;
@@ -53,7 +57,7 @@
         "-keep class " + CLASS_NAME,
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -86,7 +90,7 @@
         "-keep class " + CLASS_NAME,
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -129,7 +133,7 @@
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
         "-keep,allowobfuscation class " + BOO,
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -167,7 +171,7 @@
         "-keep class " + CLASS_NAME,
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -199,7 +203,7 @@
         "-keep class " + CLASS_NAME,
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -238,7 +242,7 @@
         "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
         "-keep,allowobfuscation class " + BOO,
         "-dontoptimize");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -268,7 +272,7 @@
         "-identifiernamestring class " + CLASS_NAME + " { static java.lang.String sClassName; }",
         "-keep class " + CLASS_NAME + " { static java.lang.String sClassName; }",
         "-dontshrink");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -290,7 +294,7 @@
         "-keep class " + CLASS_NAME + " { static java.lang.String sClassName; }",
         "-keep,allowobfuscation class " + BOO,
         "-dontshrink");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -314,7 +318,7 @@
         "-keep class " + CLASS_NAME + " { static java.lang.String sFieldName; }",
         "-keep,allowobfuscation class " + BOO + " { <fields>; }",
         "-dontshrink");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -338,7 +342,7 @@
         "-keep class " + CLASS_NAME + " { static java.lang.String sMethodName; }",
         "-keep,allowobfuscation class " + BOO + " { <methods>; }",
         "-dontshrink");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -365,10 +369,20 @@
         "invoke-static {v0, v1}, LExample;->foo(Ljava/lang/String;Ljava/lang/String;)V",
         "return-void");
 
-    List<String> pgConfigs = ImmutableList.of(
-        "-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
-        "-keep class " + CLASS_NAME);
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector =
+        compileWithR8(
+                builder,
+                testBuilder ->
+                    testBuilder
+                        .addKeepRules(
+                            "-identifiernamestring class "
+                                + CLASS_NAME
+                                + " { static void foo(...); }",
+                            "-keep class " + CLASS_NAME)
+                        .allowDiagnosticWarningMessages())
+            .assertAllWarningMessagesMatch(
+                containsString("Cannot determine what 'Mixed/form.Boo' refers to"))
+            .inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -410,7 +424,7 @@
     List<String> pgConfigs = ImmutableList.of(
         "-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
         "-keep class " + CLASS_NAME);
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -456,7 +470,7 @@
         "-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
         "-keep class " + CLASS_NAME,
         "-keep,allowobfuscation class " + BOO);
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -510,7 +524,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep class R { *; }");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -560,7 +574,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep,allowobfuscation class R { *; }");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -617,7 +631,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep class R { *; }");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -678,7 +692,7 @@
             + "}",
         "-keep class " + CLASS_NAME,
         "-keep,allowobfuscation class R { *; }");
-    CodeInspector inspector = getInspectorAfterRunR8(builder, pgConfigs);
+    CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
     assertTrue(clazz.isPresent());
@@ -702,13 +716,17 @@
     assertNotEquals("foo", constString.getString().toString());
   }
 
-  private CodeInspector getInspectorAfterRunR8(
+  private R8TestCompileResult compileWithR8(
       SmaliBuilder builder, List<String> proguardConfigurations) throws Exception {
+    return compileWithR8(builder, testBuilder -> testBuilder.addKeepRules(proguardConfigurations));
+  }
+
+  private R8TestCompileResult compileWithR8(
+      SmaliBuilder builder, ThrowableConsumer<R8FullTestBuilder> configuration) throws Exception {
     return testForR8(Backend.DEX)
         .addProgramDexFileData(builder.compile())
-        .addKeepRules(proguardConfigurations)
+        .apply(configuration)
         .debug()
-        .compile()
-        .inspector();
+        .compile();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
new file mode 100644
index 0000000..9a55cee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 IntersectionLambdaTest extends TestBase {
+
+  private static final String[] EXPECTED = new String[] {"Lambda.foo", "J.bar", "K.baz"};
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public IntersectionLambdaTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+    testForRuntime(parameters)
+        .addInnerClasses(IntersectionLambdaTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(IntersectionLambdaTest.class)
+        .enableMergeAnnotations()
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @FunctionalInterface
+  @NeverMerge
+  public interface I {
+    void foo();
+  }
+
+  public interface J {
+    default void bar() {
+      System.out.println("J.bar");
+    }
+  }
+
+  public interface K {
+    default void baz() {
+      System.out.println("K.baz");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      callI((I & J & K) () -> System.out.println("Lambda.foo"));
+    }
+
+    private static void callI(I i) {
+      i.foo();
+      ((J) i).bar();
+      ((K) i).baz();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java b/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java
new file mode 100644
index 0000000..f784a8f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IntersectionWithInheritanceLambdaTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 IntersectionWithInheritanceLambdaTest extends TestBase {
+
+  private static final String[] EXPECTED = new String[] {"Lambda.foo", "J.bar", "K.baz"};
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public IntersectionWithInheritanceLambdaTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+    testForRuntime(parameters)
+        .addInnerClasses(IntersectionWithInheritanceLambdaTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(IntersectionWithInheritanceLambdaTest.class)
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public interface J {
+    default void bar() {
+      System.out.println("J.bar");
+    }
+  }
+
+  public interface K extends J {
+    void foo();
+
+    default void baz() {
+      System.out.println("K.baz");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      callFooBarBaz(() -> System.out.println("Lambda.foo"));
+    }
+
+    private static void callFooBarBaz(K k) {
+      k.foo();
+      k.bar();
+      k.baz();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java b/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
index 982267f..57bd145 100644
--- a/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InvalidObfuscationEntryTest.java
@@ -48,7 +48,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public InvalidObfuscationEntryTest(TestParameters parameters) {
@@ -65,13 +65,10 @@
         .addKeepRules("-obfuscationdictionary " + dictionary.toString())
         .addKeepAllClassesRuleWithAllowObfuscation()
         .addKeepMainRule(Main.class)
-        .setMinApi(parameters.getRuntime())
+        .allowDiagnosticInfoMessages()
+        .setMinApi(parameters.getApiLevel())
         .compile()
-        .inspectDiagnosticMessages(
-            testDiagnosticMessages -> {
-              testDiagnosticMessages.assertInfoMessageThatMatches(
-                  containsString("Invalid character"));
-            })
+        .assertInfoMessageThatMatches(containsString("Invalid character"))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello from a", "Hello from b")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index 7828143..daa21a4 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.naming;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -67,12 +68,16 @@
   public void test_example3() throws Exception {
     TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
     String mainClassName = ex3.getClassName();
-    TestCompileResult result = testForR8(Backend.DEX)
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .addKeepMainRule(mainClassName)
-        .minification(minification)
-        .compile();
+    TestCompileResult result =
+        testForR8(Backend.DEX)
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .addKeepMainRule(mainClassName)
+            .allowDiagnosticWarningMessages()
+            .minification(minification)
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
     CodeInspector codeInspector = result.inspector();
     MethodSubject main = codeInspector.clazz(ex3.getClassName()).mainMethod();
     assertThat(main, isPresent());
@@ -116,18 +121,23 @@
       String targetFieldName,
       String targetMethodName) throws Exception {
     String mainClassName = testMain.getClassName();
-    TestRunResult result = testForR8(Backend.DEX)
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .enableProguardTestOptions()
-        .addKeepMainRule(mainClassName)
-        .addKeepRules(StringUtils.lines(
-            "-neverclassinline class **." + targetClassName,
-            "-nevermerge class **." + targetClassName,
-            "-neverinline class **." + targetClassName + " { <methods>; }"
-        ))
-        .minification(minification)
-        .run(mainClassName);
+    TestRunResult result =
+        testForR8(Backend.DEX)
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .enableProguardTestOptions()
+            .addKeepMainRule(mainClassName)
+            .addKeepRules(
+                StringUtils.lines(
+                    "-neverclassinline class **." + targetClassName,
+                    "-nevermerge class **." + targetClassName,
+                    "-neverinline class **." + targetClassName + " { <methods>; }"))
+            .allowDiagnosticWarningMessages()
+            .minification(minification)
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .run(mainClassName);
     CodeInspector codeInspector = result.inspector();
 
     MethodSubject main = codeInspector.clazz(testMain.getClassName()).mainMethod();
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
index 44e95ec..a391a21 100644
--- a/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
@@ -4,13 +4,7 @@
 
 package com.android.tools.r8.proguard.configuration;
 
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.TestBase;
-import java.util.List;
 import org.junit.Test;
 
 public class UnusedKeepRuleTest extends TestBase {
@@ -19,20 +13,8 @@
   public void test() throws Exception {
     testForR8(Backend.DEX)
         .addKeepRules("-keep class NotPresent")
-        .addOptionsModification(
-            options -> options.testing.reportUnusedProguardConfigurationRules = true)
         .allowUnusedProguardConfigurationRules()
         .compile()
-        .inspectDiagnosticMessages(
-            messages -> {
-              messages.assertOnlyInfos();
-              List<Diagnostic> infos = messages.getInfos();
-              assertEquals(1, infos.size());
-
-              Diagnostic info = infos.get(0);
-              assertThat(
-                  info.getDiagnosticMessage(),
-                  containsString("Proguard configuration rule does not match anything"));
-            });
+        .assertInfosCount(1);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java b/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
index 6da44a5..bb37f6f 100644
--- a/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/rules/InnerClassNameSeparatorTest.java
@@ -7,6 +7,7 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
@@ -27,7 +28,7 @@
   @Parameterized.Parameters(name = "{0}, separator: {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(), ImmutableList.of("$", "."));
+        getTestParameters().withAllRuntimesAndApiLevels().build(), ImmutableList.of("$", "."));
   }
 
   public InnerClassNameSeparatorTest(TestParameters parameters, String separator) {
@@ -37,17 +38,23 @@
 
   @Test
   public void testR8() throws Exception {
-    TestRunResult<?> result =
-        runTest(
-            testForR8(parameters.getBackend())
-                .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
-                .addOptionsModification(
-                    options -> {
-                      if (separator.equals(".")) {
-                        // R8 currently does not recognize the '.' as an inner class name separator.
-                        options.testing.allowUnusedProguardConfigurationRules = true;
-                      }
-                    }));
+    R8TestRunResult result =
+        testForR8(parameters.getBackend())
+            .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
+            .allowUnusedProguardConfigurationRules(separator.equals("."))
+            .addProgramClassesAndInnerClasses(InnerClassNameSeparatorTestClass.class)
+            .addKeepMainRule(InnerClassNameSeparatorTestClass.class)
+            .addKeepRules(
+                "-keep,allowobfuscation class "
+                    + InnerClassNameSeparatorTestClass.class.getTypeName()
+                    + separator
+                    + InnerClassNameSeparatorTestClass.Inner.class.getSimpleName()
+                    + " {",
+                "  <init>(...);",
+                "}")
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .run(parameters.getRuntime(), InnerClassNameSeparatorTestClass.class);
     if (separator.equals("$")) {
       result.assertSuccessWithOutputLines("Hello world!");
     } else {
@@ -73,7 +80,7 @@
                 + " {",
             "  <init>(...);",
             "}")
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), InnerClassNameSeparatorTestClass.class);
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
index db1864c..2bd5e75 100644
--- a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
+++ b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.resolution;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -47,9 +49,11 @@
         .enableInliningAnnotations()
         .addKeepClassRules(ProgramClass.class)
         .addKeepMainRule(ProgramTestRunnerWithoutPhi.class)
+        .allowDiagnosticWarningMessages()
         .setMinApi(parameters.getApiLevel())
         .debug()
         .compile()
+        .assertAllWarningMessagesMatch(containsString("extends program class"))
         .addRunClasspathClasses(LibraryClass.class)
         .run(parameters.getRuntime(), ProgramTestRunnerWithoutPhi.class)
         .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
@@ -64,8 +68,10 @@
         .enableInliningAnnotations()
         .addKeepClassRules(ProgramClass.class)
         .addKeepMainRule(ProgramTestRunnerWithPhi.class)
+        .allowDiagnosticWarningMessages()
         .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllWarningMessagesMatch(containsString("extends program class"))
         .addRunClasspathClasses(LibraryClass.class)
         .run(parameters.getRuntime(), ProgramTestRunnerWithPhi.class)
         .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index d2a1ee6..609b506 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.resolution.interfacetargets;
 
-import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -49,7 +49,8 @@
   public void testResolution() throws Exception {
     assumeTrue(parameters.useRuntimeAsNoneRuntime());
     AppInfoWithLiveness appInfo =
-        computeAppViewWithLiveness(buildClasses(I.class, A.class, Main.class).build(), Main.class)
+        computeAppViewWithLiveness(
+                buildClasses(I.class, J.class, A.class, Main.class).build(), Main.class)
             .appInfo();
     DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
     ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
@@ -59,8 +60,7 @@
             .collect(Collectors.toSet());
     ImmutableSet<String> expected =
         ImmutableSet.of(A.class.getTypeName() + ".bar", J.class.getTypeName() + ".bar");
-    // TODO(b/148168065): Correct incorrect target lookup.
-    // assertEquals(expected, targets);
+    assertEquals(expected, targets);
   }
 
   @Test
@@ -81,9 +81,7 @@
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
-    // TODO(b/148168065): Correct incorrect target lookup.
-    //    .assertSuccessWithOutputLines(EXPECTED);
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
   @FunctionalInterface
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
new file mode 100644
index 0000000..65448a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class InvokeInterfaceClInitTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  static {
+  }
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InvokeInterfaceClInitTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testResolution() throws Exception {
+    assumeTrue(parameters.useRuntimeAsNoneRuntime());
+    AppInfoWithLiveness appInfo =
+        computeAppViewWithLiveness(
+                buildClasses(A.class, B.class)
+                    .addClassProgramData(transformI(), transformMain())
+                    .build(),
+                Main.class)
+            .appInfo();
+    DexMethod method = buildNullaryVoidMethod(I.class, "<clinit>", appInfo.dexItemFactory());
+    Assert.assertThrows(
+        AssertionError.class,
+        () -> {
+          appInfo.resolveMethod(method.holder, method).lookupInterfaceTargets(appInfo);
+        });
+  }
+
+  private Matcher<String> getExpected() {
+    if (parameters.getRuntime().isCf()) {
+      Matcher<String> expected = containsString("java.lang.VerifyError");
+      // JDK 9 and 11 output VerifyError or ClassFormatError non-deterministically.
+      if (parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
+        expected = CoreMatchers.anyOf(expected, containsString("java.lang.ClassFormatError"));
+      }
+      return expected;
+    }
+    assert parameters.getRuntime().isDex();
+    DexRuntime dexRuntime = parameters.getRuntime().asDex();
+    if (dexRuntime.getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
+      return containsString("NoSuchMethodError");
+    }
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+      return containsString("java.lang.ClassNotFoundException");
+    }
+    return containsString("java.lang.VerifyError");
+  }
+
+  @Test
+  public void testRuntimeClInit()
+      throws IOException, CompilationFailedException, ExecutionException {
+    testForRuntime(parameters)
+        .addProgramClasses(A.class, B.class)
+        .addProgramClassFileData(transformMain(), transformI())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(getExpected());
+  }
+
+  @Test
+  public void testR8ClInit() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, B.class)
+        .addProgramClassFileData(transformMain(), transformI())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(getExpected());
+  }
+
+  private byte[] transformI() throws IOException {
+    return transformer(I.class)
+        .addClassTransformer(
+            new ClassTransformer() {
+              @Override
+              public MethodVisitor visitMethod(
+                  int access,
+                  String name,
+                  String descriptor,
+                  String signature,
+                  String[] exceptions) {
+                return super.visitMethod(access, "<clinit>", descriptor, signature, exceptions);
+              }
+            })
+        .transform();
+  }
+
+  private byte[] transformMain() throws IOException {
+    return transformer(Main.class)
+        .transformMethodInsnInMethod(
+            "callClInit",
+            (opcode, owner, name, descriptor, isInterface, continuation) ->
+                continuation.apply(opcode, owner, "<clinit>", descriptor, isInterface))
+        .transform();
+  }
+
+  public interface I {
+
+    default void foo() { // <-- will be rewritten to <clinit>
+      System.out.println("I.foo");
+    }
+  }
+
+  public static class A implements I {}
+
+  public static class B implements I {}
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      callClInit(args.length == 0 ? new A() : new B());
+    }
+
+    private static void callClInit(I i) {
+      i.foo(); // <-- will be i.<clinit>()
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
new file mode 100644
index 0000000..ede5785
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+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 InvokeInterfaceWithStaticTargetTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InvokeInterfaceWithStaticTargetTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testResolution() throws Exception {
+    assumeTrue(parameters.getRuntime().equals(TestRuntime.getDefaultJavaRuntime()));
+    AppInfoWithLiveness appInfo =
+        computeAppViewWithLiveness(
+                buildClasses(A.class, I.class).addClassProgramData(transformMain()).build(),
+                Main.class)
+            .appInfo();
+    DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
+    Assert.assertThrows(
+        AssertionError.class,
+        () -> appInfo.resolveMethod(method.holder, method).lookupInterfaceTargets(appInfo));
+  }
+
+  @Test
+  public void testRuntimeClInit()
+      throws IOException, CompilationFailedException, ExecutionException {
+    testForRuntime(parameters)
+        .addProgramClasses(A.class, I.class)
+        .addProgramClassFileData(transformMain())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(containsString(getExpected()));
+  }
+
+  @Test
+  public void testR8ClInit() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, I.class)
+        .addProgramClassFileData(transformMain())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(containsString(getExpected()));
+  }
+
+  private String getExpected() {
+    return parameters.isCfRuntime()
+            || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+        ? "IncompatibleClassChangeError"
+        : "NoSuchMethodError";
+  }
+
+  private byte[] transformMain() throws IOException {
+    return transformer(Main.class)
+        .transformMethodInsnInMethod(
+            "callFooBar",
+            (opcode, owner, name, descriptor, isInterface, continuation) -> {
+              if (name.equals("notify")) {
+                continuation.apply(
+                    INVOKEINTERFACE,
+                    DescriptorUtils.getBinaryNameFromJavaType(I.class.getTypeName()),
+                    "bar",
+                    descriptor,
+                    true);
+              } else {
+                continuation.apply(opcode, owner, name, descriptor, isInterface);
+              }
+            })
+        .transform();
+  }
+
+  public interface I {
+
+    void foo();
+
+    static void bar() {
+      System.out.println("I.bar");
+    }
+  }
+
+  public static class A implements I {
+
+    @Override
+    public void foo() {
+      System.out.println("A.foo");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      callFooBar(args.length == 0 ? () -> System.out.println("Lambda.foo") : new A());
+    }
+
+    public static void callFooBar(I i) {
+      i.foo();
+      i.notify(); // <-- will be i.bar()
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 5d2a2d8..13190d9 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -10,6 +10,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
 
@@ -86,8 +87,11 @@
         .addKeepAttributes("SourceFile", "LineNumberTable")
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(MAIN)
+        .allowDiagnosticWarningMessages()
         .noMinification()
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), MAIN)
         .assertFailureWithErrorThatMatches(containsString("main"))
         .inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index e2fe820..2d9f47b 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -11,6 +11,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
 
@@ -81,9 +82,12 @@
         .addProgramFiles(compilationResults.apply(parameters.getRuntime()))
         .addProgramFiles(ToolHelper.getKotlinStdlibJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
+        .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(main)
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), main)
         .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
         .inspectStackTrace(
@@ -106,9 +110,12 @@
         .addProgramFiles(compilationResults.apply(parameters.getRuntime()))
         .addProgramFiles(ToolHelper.getKotlinStdlibJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
+        .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(main)
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), main)
         .assertFailureWithErrorThatMatches(containsString("inlineExceptionInstance"))
         .inspectStackTrace(
@@ -131,9 +138,12 @@
         .addProgramFiles(compilationResults.apply(parameters.getRuntime()))
         .addProgramFiles(ToolHelper.getKotlinStdlibJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
+        .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(main)
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), main)
         .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
         .inspectStackTrace(
@@ -158,9 +168,12 @@
         .addProgramFiles(compilationResults.apply(parameters.getRuntime()))
         .addProgramFiles(ToolHelper.getKotlinStdlibJar())
         .addKeepAttributes("SourceFile", "LineNumberTable")
+        .allowDiagnosticWarningMessages()
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(main)
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), main)
         .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
         .inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index 5143949..b299b76 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.rewrite;
 
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -26,7 +29,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public JavaScriptScriptEngineTest(TestParameters parameters) {
@@ -41,7 +44,7 @@
     assumeTrue("Only run D8 for dex backend", parameters.isDexRuntime());
     testForD8()
         .addInnerClasses(JavaScriptScriptEngineTest.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .apply(this::addRhinoForAndroid)
         .compile()
         .run(parameters.getRuntime(), TestClassWithExplicitRhinoScriptEngineRegistration.class)
@@ -53,15 +56,21 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(JavaScriptScriptEngineTest.class)
         .addKeepMainRule(TestClass.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .apply(
             b -> {
               if (parameters.isDexRuntime()) {
                 addRhinoForAndroid(b);
                 addKeepRulesForAndroidRhino(b);
+                b.allowDiagnosticWarningMessages();
               }
             })
         .compile()
+        .assertAllWarningMessagesMatch(
+            anyOf(
+                containsString("Missing class:"),
+                containsString("required for default or static interface methods desugaring"),
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(
             parameters.isCfRuntime() ? EXPECTED_NASHORN_OUTPUT : EXPECTED_RHINO_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index 4e451ee..c7037ca 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -4,12 +4,14 @@
 
 package com.android.tools.r8.rewrite;
 
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DataEntryResource;
-import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.origin.Origin;
@@ -41,7 +43,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public ScriptEngineTest(TestParameters parameters) {
@@ -51,33 +53,37 @@
   @Test
   public void test() throws IOException, CompilationFailedException, ExecutionException {
     Path path = temp.newFile("out.zip").toPath();
-    R8FullTestBuilder builder =
-        testForR8(parameters.getBackend())
-            .addInnerClasses(ScriptEngineTest.class)
-            .addKeepMainRule(TestClass.class)
-            .setMinApi(parameters.getRuntime())
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(MyScriptEngine1FactoryImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
-                    Origin.unknown()))
-            .addDataEntryResources(
-                DataEntryResource.fromBytes(
-                    StringUtils.lines(MyScriptEngine2FactoryImpl.class.getTypeName()).getBytes(),
-                    "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
-                    Origin.unknown()))
-            .apply(
-                b -> {
-                  if (parameters.isDexRuntime()) {
-                    addRhinoForAndroid(b);
-                  }
-                })
-            // TODO(b/136633154): This should work both with and without -dontobfuscate.
-            .noMinification()
-            // TODO(b/136633154): This should work both with and without -dontshrink.
-            .noTreeShaking();
-    builder
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ScriptEngineTest.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .addDataEntryResources(
+            DataEntryResource.fromBytes(
+                StringUtils.lines(MyScriptEngine1FactoryImpl.class.getTypeName()).getBytes(),
+                "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
+                Origin.unknown()))
+        .addDataEntryResources(
+            DataEntryResource.fromBytes(
+                StringUtils.lines(MyScriptEngine2FactoryImpl.class.getTypeName()).getBytes(),
+                "META-INF/services/" + ScriptEngineFactory.class.getTypeName(),
+                Origin.unknown()))
+        .apply(
+            b -> {
+              if (parameters.isDexRuntime()) {
+                addRhinoForAndroid(b);
+                b.allowDiagnosticWarningMessages();
+              }
+            })
+        // TODO(b/136633154): This should work both with and without -dontobfuscate.
+        .noMinification()
+        // TODO(b/136633154): This should work both with and without -dontshrink.
+        .noTreeShaking()
         .compile()
+        .assertAllWarningMessagesMatch(
+            anyOf(
+                containsString("Missing class:"),
+                containsString("it is required for default or static interface methods desugaring"),
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
         .writeToZip(path)
         .run(parameters.getRuntime(), TestClass.class)
         // TODO(b/136633154): This should provide 2 script engines on both runtimes. The use of
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
index 6f18cdc..302cb28 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalStaticFieldsTest.java
@@ -68,8 +68,7 @@
                   mainMethod
                       .streamInstructions()
                       .noneMatch(i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
-              // TODO(b/138913138): effectively final, and default value is set.
-              assertFalse(
+              assertTrue(
                   mainMethod
                       .streamInstructions()
                       .noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
@@ -97,8 +96,7 @@
                   mainMethod
                       .streamInstructions()
                       .noneMatch(i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
-              // TODO(b/138913138): effectively final, and default value is set.
-              assertFalse(
+              assertTrue(
                   mainMethod
                       .streamInstructions()
                       .noneMatch(i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index b42be41..5b119ce 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -7,6 +7,7 @@
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.core.IsNot.not;
 
 import com.android.tools.r8.D8TestRunResult;
@@ -79,7 +80,9 @@
   @Parameters(name = "{0}, mode: {1}, use interface: {2}")
   public static Collection<Object[]> parameters() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(), Mode.values(), BooleanUtils.values());
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        Mode.values(),
+        BooleanUtils.values());
   }
 
   @Test
@@ -171,7 +174,8 @@
     jasminBuilder.writeJar(inputJar);
 
     if (parameters.isCfRuntime()) {
-      TestRunResult<?> jvmResult = testForJvm().addClasspath(inputJar).run(mainClass.name);
+      TestRunResult<?> jvmResult =
+          testForJvm().addClasspath(inputJar).run(parameters.getRuntime(), mainClass.name);
       checkTestRunResult(jvmResult, Compiler.JAVAC);
 
       ProguardTestRunResult proguardResult =
@@ -184,10 +188,12 @@
     } else {
       assert parameters.isDexRuntime();
 
-      DXTestRunResult dxResult = testForDX().addProgramFiles(inputJar).run(mainClass.name);
+      DXTestRunResult dxResult =
+          testForDX().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
       checkTestRunResult(dxResult, Compiler.DX);
 
-      D8TestRunResult d8Result = testForD8().addProgramFiles(inputJar).run(mainClass.name);
+      D8TestRunResult d8Result =
+          testForD8().addProgramFiles(inputJar).run(parameters.getRuntime(), mainClass.name);
       checkTestRunResult(d8Result, Compiler.D8);
     }
 
@@ -205,7 +211,14 @@
                     options.testing.allowTypeErrors = true;
                   }
                 })
-            .setMinApi(parameters.getRuntime())
+            .allowDiagnosticWarningMessages(
+                mode == Mode.INVOKE_UNVERIFIABLE_METHOD && !useInterface)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo(
+                    "The method `void UnverifiableClass.unverifiableMethod()` does not type check"
+                        + " and will be assumed to be unreachable."))
             .run(parameters.getRuntime(), mainClass.name);
     checkTestRunResult(r8Result, Compiler.R8);
 
@@ -224,7 +237,14 @@
                   }
                   options.enableUninstantiatedTypeOptimizationForInterfaces = true;
                 })
-            .setMinApi(parameters.getRuntime())
+            .allowDiagnosticWarningMessages(
+                mode == Mode.INVOKE_UNVERIFIABLE_METHOD && !useInterface)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo(
+                    "The method `void UnverifiableClass.unverifiableMethod()` does not type check"
+                        + " and will be assumed to be unreachable."))
             .run(parameters.getRuntime(), mainClass.name);
     checkTestRunResult(
         r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
diff --git a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
index 28e04fd..cd153f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
@@ -44,7 +45,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().build();
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
   public PreserveDesugaredLambdaTest(TestParameters parameters) {
@@ -58,16 +59,18 @@
         testForR8(parameters.getBackend())
             .addProgramClasses(Interface.class, A.class)
             .addKeepAllClassesRule()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .compile();
     // A is not passed in to ensure the Enqueuer is not tracing through classpath to see the use of
-    // computeFoo().s
+    // computeFoo().
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class)
         .addClasspathClasses(Interface.class)
         .addLibraryFiles(TestBase.runtimeJar(parameters.getBackend()))
         .addKeepAllClassesRule()
-        .setMinApi(parameters.getRuntime())
+        .allowDiagnosticWarningMessages(
+            parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .addRunClasspathFiles(libraryCompileResult.writeToZip())
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java b/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
index 4c9b057..3355fab 100644
--- a/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/UsageInformationConsumerTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
@@ -11,8 +12,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.StringUtils;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -49,16 +48,16 @@
 
   @Test
   public void testRule() throws Exception {
-    ByteArrayOutputStream out = new ByteArrayOutputStream();
     testForR8(parameters.getBackend())
-        .redirectStdOut(new PrintStream(out))
+        .collectStdout()
         .addProgramClasses(TestClass.class, UnusedClass.class)
         .addKeepClassAndMembersRules(TestClass.class)
         .addKeepRules("-printusage")
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertStdoutThatMatches(equalTo(StringUtils.lines(UnusedClass.class.getTypeName())))
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED);
-    assertEquals(StringUtils.lines(UnusedClass.class.getTypeName()), out.toString());
   }
 
   static class UnusedClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java b/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java
new file mode 100644
index 0000000..4f2943f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/VerticalClassMergerInvokeSpecialInConstructorTest.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a reproduction of b/148313389, where we rewrite the super-call in B.<init>. */
+@RunWith(Parameterized.class)
+public class VerticalClassMergerInvokeSpecialInConstructorTest extends TestBase {
+
+  public static final String[] EXPECTED =
+      new String[] {"A.<init>", "B.<init>", "A.foo", "C.<init>", "B.foo"};
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public VerticalClassMergerInvokeSpecialInConstructorTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+    testForRuntime(parameters)
+        .addInnerClasses(VerticalClassMergerInvokeSpecialInConstructorTest.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(VerticalClassMergerInvokeSpecialInConstructorTest.class)
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(A.class)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public abstract static class A {
+
+    public A() {
+      System.out.println("A.<init>");
+    }
+
+    public void foo() {
+      System.out.println("A.foo");
+    }
+  }
+
+  public static class B extends A {
+
+    public B() {
+      System.out.println("B.<init>");
+      super.foo(); // <-- In b/148313389, this was rewritten to invoke-direct B.foo.
+    }
+
+    @Override
+    public void foo() {
+      System.out.println("B.foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class C extends B {
+
+    public C() {
+      System.out.println("C.<init>");
+      super.foo();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new C();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 5c6a305..84ceffa 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -62,7 +63,7 @@
   @Parameterized.Parameters(name = "{0} target: {1} minify: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(),
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
         KotlinTargetVersion.values(),
         BooleanUtils.values());
   }
@@ -87,20 +88,22 @@
 
   @Test
   public void b120951621_keepAll() throws Exception {
-    CodeInspector inspector = testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .addKeepMainRule(MAIN_CLASS_NAME)
-        .addKeepRules(KEEP_ANNOTATIONS)
-        .addKeepRules(
-            "-keep @interface " + ANNOTATION_NAME + " {",
-            "  *;",
-            "}"
-        )
-        .minification(minify)
-        .setMinApi(parameters.getRuntime())
-        .run(parameters.getRuntime(), MAIN_CLASS_NAME)
-        .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .addKeepMainRule(MAIN_CLASS_NAME)
+            .addKeepRules(KEEP_ANNOTATIONS)
+            .addKeepRules("-keep @interface " + ANNOTATION_NAME + " {", "  *;", "}")
+            .allowDiagnosticWarningMessages()
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertSuccessWithOutput(JAVA_OUTPUT)
+            .inspector();
     ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
     assertThat(clazz, isPresent());
     assertThat(clazz, not(isRenamed()));
@@ -126,20 +129,25 @@
 
   @Test
   public void b120951621_partiallyKeep() throws Exception {
-    CodeInspector inspector = testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .addKeepMainRule(MAIN_CLASS_NAME)
-        .addKeepRules(KEEP_ANNOTATIONS)
-        .addKeepRules(
-            "-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
-            "  java.lang.String *f2();",
-            "}"
-        )
-        .minification(minify)
-        .setMinApi(parameters.getRuntime())
-        .run(parameters.getRuntime(), MAIN_CLASS_NAME)
-        .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .addKeepMainRule(MAIN_CLASS_NAME)
+            .addKeepRules(KEEP_ANNOTATIONS)
+            .addKeepRules(
+                "-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
+                "  java.lang.String *f2();",
+                "}")
+            .allowDiagnosticWarningMessages()
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertSuccessWithOutput(JAVA_OUTPUT)
+            .inspector();
     ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
     assertThat(clazz, isPresent());
     assertEquals(minify, clazz.isRenamed());
@@ -163,15 +171,21 @@
 
   @Test
   public void b120951621_keepAnnotation() throws Exception {
-    CodeInspector inspector = testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .addKeepMainRule(MAIN_CLASS_NAME)
-        .addKeepRules(KEEP_ANNOTATIONS)
-        .minification(minify)
-        .setMinApi(parameters.getRuntime())
-        .run(parameters.getRuntime(), MAIN_CLASS_NAME)
-        .assertSuccessWithOutput(JAVA_OUTPUT).inspector();
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .addKeepMainRule(MAIN_CLASS_NAME)
+            .addKeepRules(KEEP_ANNOTATIONS)
+            .allowDiagnosticWarningMessages()
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertSuccessWithOutput(JAVA_OUTPUT)
+            .inspector();
     ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
     assertThat(clazz, isPresent());
     assertEquals(minify, clazz.isRenamed());
@@ -195,14 +209,20 @@
 
   @Test
   public void b120951621_noKeep() throws Exception {
-    CodeInspector inspector = testForR8(parameters.getBackend())
-        .addProgramFiles(getKotlinJarFile(FOLDER))
-        .addProgramFiles(getJavaJarFile(FOLDER))
-        .addKeepMainRule(MAIN_CLASS_NAME)
-        .minification(minify)
-        .setMinApi(parameters.getRuntime())
-        .run(parameters.getRuntime(), MAIN_CLASS_NAME)
-        .assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION).inspector();
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(getKotlinJarFile(FOLDER))
+            .addProgramFiles(getJavaJarFile(FOLDER))
+            .addKeepMainRule(MAIN_CLASS_NAME)
+            .allowDiagnosticWarningMessages()
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION)
+            .inspector();
     ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
     assertThat(clazz, isPresent());
     assertEquals(minify, clazz.isRenamed());
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 9e92530..acb1e86 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -5,11 +5,13 @@
 package com.android.tools.r8.shaking.annotations.b137392797;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -70,8 +72,12 @@
             "com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
         .addKeepMainRule(TestClass.class)
         .addKeepAttributes("*Annotation*")
-        .setMinApi(parameters.getRuntime())
+        .allowDiagnosticWarningMessages(
+            parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+        .setMinApi(parameters.getApiLevel())
         .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("required for default or static interface methods desugaring"))
         .inspect(this::checkEnumUses)
         .run(parameters.getRuntime(), TestClass.class, "com.squareup.demo.myapplication.Test")
         .assertSuccessWithOutputLines(
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 fd2696f..0d9fa5e 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
@@ -14,6 +14,7 @@
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper;
@@ -274,11 +275,10 @@
         AndroidApiLevel.O_MR1,
         AndroidApiLevel.O_MR1,
         expectedResultForNative(AndroidApiLevel.O_MR1),
-        builder ->
-            builder.addOptionsModification(
-                options ->
-                    // android.os.Build$VERSION only exists in the Android runtime.
-                    options.testing.allowUnusedProguardConfigurationRules = backend == Backend.CF),
+        builder -> {
+          // android.os.Build$VERSION only exists in the Android runtime.
+          builder.allowUnusedProguardConfigurationRules(backend == Backend.CF);
+        },
         this::compatCodeNotPresent,
         ImmutableList.of(
             "-assumevalues class android.os.Build$VERSION { public static final int SDK_INT return "
@@ -307,9 +307,7 @@
           AndroidApiLevel.O_MR1,
           AndroidApiLevel.O_MR1,
           expectedResultForNative(AndroidApiLevel.O_MR1),
-          builder ->
-              builder.addOptionsModification(
-                  options -> options.testing.allowUnusedProguardConfigurationRules = true),
+          builder -> builder.allowUnusedProguardConfigurationRules(backend == Backend.CF),
           this::compatCodePresent,
           ImmutableList.of(rule),
           SynthesizedRule.NOT_PRESENT);
@@ -332,9 +330,7 @@
           AndroidApiLevel.O_MR1,
           AndroidApiLevel.O_MR1,
           expectedResultForNative(AndroidApiLevel.O_MR1),
-          builder ->
-              builder.addOptionsModification(
-                  options -> options.testing.allowUnusedProguardConfigurationRules = true),
+          R8TestBuilder::allowUnusedProguardConfigurationRules,
           this::compatCodeNotPresent,
           ImmutableList.of(rule),
           SynthesizedRule.PRESENT);
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 6d0d767..4ce41ce 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.shaking.b134858535;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -25,7 +27,9 @@
         .addProgramClassFileData(EventPublisher$bDump.dump())
         .addKeepClassRules(Interface.class)
         .addKeepMainRule(Main.class)
+        .allowDiagnosticInfoMessages()
         .setMinApi(AndroidApiLevel.L)
-        .compile();
+        .compile()
+        .assertAllWarningMessagesMatch(containsString("Unrecognized Kotlin lambda"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
index ab37cb0..27bb092 100644
--- a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
@@ -8,9 +8,14 @@
 import com.android.tools.r8.NeverMerge;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 @NeverMerge
 interface I {
@@ -34,68 +39,82 @@
   }
 }
 
+@RunWith(Parameterized.class)
 public class KeepRuleWarningTest extends TestBase {
+
   private static final Class<?> MAIN = KeepRuleWarningTestRunner.class;
   private static final String EXPECTED_OUTPUT = StringUtils.lines("static::foo", "default::bar");
 
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.L).build();
+  }
+
+  public KeepRuleWarningTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   @Test
   public void test_allMethods() throws Exception {
-    testForR8(Backend.DEX)
+    testForR8(parameters.getBackend())
         .addProgramClasses(I.class, C.class, MAIN)
-        .setMinApi(AndroidApiLevel.L)
+        .setMinApi(parameters.getApiLevel())
         .enableMergeAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep interface **.I { <methods>; }")
         .compile()
         .inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
-        .run(MAIN)
+        .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   @Test
   public void test_asterisk() throws Exception {
-    testForR8(Backend.DEX)
+    testForR8(parameters.getBackend())
         .addProgramClasses(I.class, C.class, MAIN)
-        .setMinApi(AndroidApiLevel.L)
+        .setMinApi(parameters.getApiLevel())
         .enableMergeAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep interface **.I { *(); }")
         .compile()
         .inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
-        .run(MAIN)
+        .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   @Test
   public void test_stillNotSpecific() throws Exception {
-    testForR8(Backend.DEX)
+    testForR8(parameters.getBackend())
         .addProgramClasses(I.class, C.class, MAIN)
-        .setMinApi(AndroidApiLevel.L)
+        .setMinApi(parameters.getApiLevel())
         .enableMergeAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep interface **.I { *** f*(); }")
         .compile()
         .inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
-        .run(MAIN)
+        .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   @Test
   public void test_specific() throws Exception {
-    testForR8(Backend.DEX)
+    testForR8(parameters.getBackend())
         .addProgramClasses(I.class, C.class, MAIN)
-        .setMinApi(AndroidApiLevel.L)
+        .setMinApi(parameters.getApiLevel())
         .enableMergeAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep interface **.I { static void foo(); }")
+        .allowDiagnosticWarningMessages()
         .compile()
-        .inspectDiagnosticMessages(m -> {
-          m.assertWarningsCount(1)
-              .assertWarningMessageThatMatches(containsString("static void foo()"))
-              .assertWarningMessageThatMatches(containsString("is ignored"))
-              .assertWarningMessageThatMatches(containsString("will be desugared"));
-        })
-        .run(MAIN)
+        .inspectDiagnosticMessages(
+            m ->
+                m.assertWarningsCount(1)
+                    .assertWarningMessageThatMatches(containsString("static void foo()"))
+                    .assertWarningMessageThatMatches(containsString("is ignored"))
+                    .assertWarningMessageThatMatches(containsString("will be desugared")))
+        .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index 8cb1cf0..445e24b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -47,6 +47,10 @@
   }
 
   private TestShrinkerBuilder<?, ?, ?, ?, ?> getTestBuilder() {
+    return getTestBuilder(false);
+  }
+
+  private TestShrinkerBuilder<?, ?, ?, ?, ?> getTestBuilder(boolean allowDiagnosticInfoMessages) {
     switch (shrinker) {
       case PROGUARD6:
         assertTrue(parameters.isCfRuntime());
@@ -54,7 +58,7 @@
       case R8:
         return testForR8(parameters.getBackend())
             .addTestingAnnotationsAsProgramClasses()
-            .allowUnusedProguardConfigurationRules()
+            .allowUnusedProguardConfigurationRules(allowDiagnosticInfoMessages)
             .enableNeverClassInliningAnnotations()
             .enableInliningAnnotations();
       default:
@@ -66,7 +70,7 @@
   public void ifOnPublic_noPublicClassForIfRule() throws Exception {
     assumeFalse(shrinker.isProguard() && parameters.isDexRuntime());
 
-    getTestBuilder()
+    getTestBuilder(shrinker.isR8())
         .addProgramClasses(CLASSES)
         .addKeepRules(
             "-repackageclasses 'top'",
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
index 54fcadf..b79d90e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
 import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -17,15 +18,32 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NeverMerge;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 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 IfRuleWithInterfaceMethodDesugaringTest extends TestBase {
 
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.M).build();
+  }
+
+  public IfRuleWithInterfaceMethodDesugaringTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
   @Test
   public void test() throws Exception {
     String expectedOutput =
@@ -34,7 +52,7 @@
     testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
 
     CodeInspector inspector =
-        testForR8(Backend.DEX)
+        testForR8(parameters.getBackend())
             .addInnerClasses(IfRuleWithInterfaceMethodDesugaringTest.class)
             .addKeepMainRule(TestClass.class)
             .addKeepRules(
@@ -50,8 +68,9 @@
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
             .enableMergeAnnotations()
-            .setMinApi(AndroidApiLevel.M)
-            .run(TestClass.class)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expectedOutput)
             .inspector();
 
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
index 37ba854..9edc64d 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/RemovedClassTestRunner.java
@@ -5,20 +5,19 @@
 
 import static com.android.tools.r8.references.Reference.classFromClass;
 import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.graphinspector.GraphInspector;
 import com.android.tools.r8.utils.graphinspector.GraphInspector.QueryNode;
 import com.google.common.collect.ImmutableList;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,15 +33,15 @@
 
   private static final String EXPECTED = StringUtils.lines("called bar");
 
-  private final Backend backend;
+  private final TestParameters parameters;
 
   @Parameters(name = "{0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public RemovedClassTestRunner(Backend backend) {
-    this.backend = backend;
+  public RemovedClassTestRunner(TestParameters parameters) {
+    this.parameters = parameters;
   }
 
   @Test
@@ -51,22 +50,24 @@
     MethodReference barMethod = methodFromMethod(CLASS.getDeclaredMethod("bar"));
     MethodReference bazMethod = methodFromMethod(CLASS.getDeclaredMethod("baz"));
 
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
     R8TestCompileResult compileResult =
-        testForR8(backend)
+        testForR8(parameters.getBackend())
             .enableGraphInspector()
             .enableInliningAnnotations()
             .addProgramClasses(CLASSES)
             .addKeepMethodRules(mainMethod)
             .addKeepRules("-whyareyoukeeping class " + REMOVED_CLASS.getTypeName())
-            .redirectStdOut(new PrintStream(baos))
-            .compile();
-    String expectedOutput = StringUtils.lines("Nothing is keeping " + REMOVED_CLASS.getTypeName());
-    String compileOutput = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    assertEquals(expectedOutput, compileOutput);
+            .setMinApi(parameters.getApiLevel())
+            .collectStdout()
+            .compile()
+            .assertStdoutThatMatches(
+                equalTo(StringUtils.lines("Nothing is keeping " + REMOVED_CLASS.getTypeName())));
 
     GraphInspector inspector =
-        compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED).graphInspector();
+        compileResult
+            .run(parameters.getRuntime(), CLASS)
+            .assertSuccessWithOutput(EXPECTED)
+            .graphInspector();
 
     // The only root should be the keep main-method rule.
     assertEquals(1, inspector.getRoots().size());
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index f4ed50c..2387e7c 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -5,7 +5,6 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -13,7 +12,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.Test;
@@ -40,7 +38,7 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  final TestParameters parameters;
+  private final TestParameters parameters;
 
   public WhyAreYouKeepingAllTest(TestParameters parameters) {
     this.parameters = parameters;
@@ -53,13 +51,13 @@
         .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
         .addKeepRuleFiles(MAIN_KEEP)
         .addKeepRules(WHY_ARE_YOU_KEEPING_ALL)
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    assertThat(baos.toString(), containsString("referenced in keep rule"));
-
-    // TODO(b/124655065): We should always know the reason for keeping.
-    // It is OK if this starts failing while the kept-graph API is incomplete, in which case replace
-    // the 'not(containsString(' by just 'containsString('.
-    assertThat(baos.toString(), not(containsString("kept for unknown reasons")));
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(containsString("referenced in keep rule"))
+        // TODO(b/124655065): We should always know the reason for keeping.
+        // It is OK if this starts failing while the kept-graph API is incomplete, in which case
+        // replace
+        // the 'not(containsString(' by just 'containsString('.
+        .assertStdoutThatMatches(not(containsString("kept for unknown reasons")));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
index 82234e7..a084679 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -5,12 +5,14 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -30,31 +32,38 @@
   private static final Path MOCKITO_INTERFACE_JAR =
       Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, M_I_PKG + FileUtils.JAR_EXTENSION);
 
-  private final Backend backend;
+  private final TestParameters parameters;
   private final boolean minify;
 
-  @Parameterized.Parameters(name = "Backend: {0} minify: {1}")
+  @Parameterized.Parameters(name = "{0}, minify: {1}")
   public static Collection<Object[]> data() {
-    return buildParameters(ToolHelper.getBackends(), BooleanUtils.values());
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
   }
 
-  public MockitoTest(Backend backend, boolean minify) {
-    this.backend = backend;
+  public MockitoTest(TestParameters parameters, boolean minify) {
+    this.parameters = parameters;
     this.minify = minify;
   }
 
   @Test
   public void b120675359_devirtualized() throws Exception {
     Path flagToKeepTestRunner = Paths.get(ToolHelper.EXAMPLES_DIR, M_I_PKG, "keep-rules.txt");
-    R8FullTestBuilder builder =
-        testForR8(backend)
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
             .addProgramFiles(MOCKITO_INTERFACE_JAR)
             .addKeepRuleFiles(flagToKeepTestRunner)
-            .minification(minify);
-    CodeInspector inspector = builder.compile().inspector();
+            .allowDiagnosticWarningMessages(
+                parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                containsString("required for default or static interface methods desugaring"))
+            .inspector();
     ClassSubject itf = inspector.clazz(M_I);
     assertThat(itf, isPresent());
-    MethodSubject mtd = itf.method("void", "onEnterForeground");
+    MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground");
     assertThat(mtd, not(isPresent()));
   }
 
@@ -62,15 +71,21 @@
   public void b120675359_conditional_keep() throws Exception {
     Path flagToKeepInterfaceConditionally =
         Paths.get(ToolHelper.EXAMPLES_DIR, M_I_PKG, "keep-rules-conditional-on-mock.txt");
-    R8FullTestBuilder builder =
-        testForR8(backend)
+    CodeInspector inspector =
+        testForR8(parameters.getBackend())
             .addProgramFiles(MOCKITO_INTERFACE_JAR)
             .addKeepRuleFiles(flagToKeepInterfaceConditionally)
-            .minification(minify);
-    CodeInspector inspector = builder.compile().inspector();
+            .allowDiagnosticWarningMessages(
+                parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+            .minification(minify)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .assertAllWarningMessagesMatch(
+                containsString("required for default or static interface methods desugaring"))
+            .inspector();
     ClassSubject itf = inspector.clazz(M_I);
     assertThat(itf, isPresent());
-    MethodSubject mtd = itf.method("void", "onEnterForeground");
+    MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground");
     assertThat(mtd, isPresent());
     assertThat(mtd, not(isRenamed()));
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
index 2914c4e..39d3aaf 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingOverriddenMethodTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
@@ -45,7 +46,6 @@
 
   private void testViaConfig(Class<?> main, Class<?> targetClass, Class<?> subClass)
       throws Exception {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
     testForR8(Backend.DEX)
         .addInnerClasses(WhyAreYouKeepingOverriddenMethodTest.class)
         .addKeepMainRule(main)
@@ -55,13 +55,12 @@
         .enableInliningAnnotations()
         .enableMergeAnnotations()
         .minification(minification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(AndroidApiLevel.B)
         // Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    assertThat(output, containsString(expectedMessage(targetClass)));
-    assertThat(output, not(containsString(expectedNotContainingMessage(subClass))));
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(containsString(expectedMessage(targetClass)))
+        .assertStdoutThatMatches(not(containsString(expectedNotContainingMessage(subClass))));
   }
 
   private void testViaConsumer(
@@ -74,7 +73,7 @@
         .enableInliningAnnotations()
         .enableMergeAnnotations()
         .minification(minification)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(AndroidApiLevel.B)
         .setKeptGraphConsumer(graphConsumer)
         .compile();
 
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
index c230c00..eeb280b 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
@@ -4,8 +4,9 @@
 
 package com.android.tools.r8.shaking.whyareyoukeeping;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.NeverInline;
@@ -77,10 +78,9 @@
         .addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
         .addKeepRules("-whyareyoukeeping class " + A.class.getTypeName())
         // Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    assertEquals(expected, output);
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(equalTo(expected));
   }
 
   @Test
@@ -108,10 +108,9 @@
         .addKeepRules("-whyareyoukeeping class " + A.class.getTypeName() + " { baz(); }")
         .addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
         // Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    assertEquals(expected + expectedPathToBaz, output);
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(equalTo(expected + expectedPathToBaz));
   }
 
   @Test
@@ -133,18 +132,15 @@
 
   @Test
   public void testNonExistentClassWhyAreYouKeepingViaProguardConfig() throws Exception {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
     testForR8(backend)
         .addProgramClasses(A.class)
         .addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
         .addKeepRules("-whyareyoukeeping class NonExistentClass")
         .allowUnusedProguardConfigurationRules()
         // Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    // Expected outcome is empty.
-    assertEquals("", output);
+        .collectStdout()
+        .compile()
+        .assertNoStdout();
   }
 
   @Test
@@ -156,10 +152,8 @@
         .addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
         .addKeepRules("-whyareyoukeeping class " + aName + " { nonExistentMethod(); }")
         // Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
-        .redirectStdOut(new PrintStream(baos))
-        .compile();
-    String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
-    // Expected outcome is empty.
-    assertFalse("b/122820741", output.isEmpty());
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(not(equalTo("")));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java b/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java
new file mode 100644
index 0000000..082c947
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ForwardingOutputStream.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+public class ForwardingOutputStream extends OutputStream {
+
+  private final List<OutputStream> listeners;
+
+  public ForwardingOutputStream(OutputStream... listeners) {
+    this.listeners = Arrays.asList(listeners);
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    for (OutputStream out : listeners) {
+      out.write(b);
+    }
+  }
+
+  @Override
+  public void write(byte[] b) throws IOException {
+    for (OutputStream out : listeners) {
+      out.write(b);
+    }
+  }
+
+  @Override
+  public void write(byte[] b, int off, int len) throws IOException {
+    for (OutputStream out : listeners) {
+      out.write(b, off, len);
+    }
+  }
+
+  @Override
+  public void flush() throws IOException {
+    for (OutputStream out : listeners) {
+      out.flush();
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    for (OutputStream out : listeners) {
+      out.close();
+    }
+  }
+}
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
index 69a9db9..9155657 100644
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -10,7 +10,7 @@
 def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True):
   utils.Print('Signing (ignore the warnings)', quiet=quiet)
   cmd = ['zip', '-d', unsigned_apk, 'META-INF/*']
-  utils.RunCmd(cmd, quiet=quiet, logging=logging)
+  utils.RunCmd(cmd, quiet=quiet, logging=logging, fail=False)
   cmd = [
     'jarsigner',
     '-sigalg', 'SHA1withRSA',
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 29f7459..c035520 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -42,7 +42,8 @@
   result.add_option('--api',
                     help='Android api level',
                     default=21,
-                    choices=[14, 15, 19, 21, 22, 23, 24, 25, 26])
+                    choices=['14', '15', '19', '21', '22', '23', '24', '25',
+                             '26'])
   result.add_option('--keystore',
                     help='Keystore used for signing',
                     default=DEFAULT_KEYSTORE)
@@ -156,7 +157,8 @@
              '--classpath', utils.get_android_jar(api),
              '--min-api', str(api)]
   command.extend(files)
-  command.append(get_guava_jar())
+  if app != 'simple':
+    command.append(get_guava_jar())
 
   utils.PrintCmd(command)
   subprocess.check_call(command)
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index c31bfea..ab01595 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -163,6 +163,10 @@
                     help='Setting the increment',
                     type='int',
                     default=32)
+  result.add_option('--print-times',
+                    help='Include timing',
+                    default=False,
+                    action='store_true')
 
   return result.parse_args(argv)
 
@@ -431,6 +435,9 @@
   if not options.ignore_java_version:
     utils.check_java_version()
 
+  if options.print_times:
+    extra_args.append('-Dcom.android.tools.r8.printtimes=1')
+
   outdir = options.out
   (version_id, data) = get_version_and_data(options)