Test completeness of profile rewriting on tivi

Bug: b/267592755
Change-Id: I4921ec85ead3f2a2a24bd7b2ae2d4a2964690412
diff --git a/build.gradle b/build.gradle
index 870b615..a6feb9b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2298,6 +2298,12 @@
         systemProperty 'runtimes', project.property('runtimes')
     }
 
+    if (project.hasProperty('art_profile_rewriting_completeness_check')) {
+        String key = 'com.android.tools.r8.artprofilerewritingcompletenesscheck'
+        String value = project.property('art_profile_rewriting_completeness_check')
+        systemProperty key, value
+    }
+
     if (project.hasProperty('slow_tests')) {
         systemProperty 'slow_tests', project.property('slow_tests')
     }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7fac411..1cc9392 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,6 +71,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.repackaging.Repackaging;
 import com.android.tools.r8.repackaging.RepackagingLens;
@@ -279,6 +280,8 @@
         SyntheticItems.collectSyntheticInputs(appView);
       }
 
+      assert ArtProfileCompletenessChecker.verify(appView);
+
       // Check for potentially having pass-through of Cf-code for kotlin libraries.
       options.enableCfByteCodePassThrough =
           options.isGeneratingClassFiles() && KotlinMetadataUtils.mayProcessKotlinMetadata(appView);
@@ -374,6 +377,7 @@
         assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness);
         assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness);
         assert appView.rootSet().verifyKeptItemsAreKept(appView);
+        assert ArtProfileCompletenessChecker.verify(appView);
         appView.rootSet().checkAllRulesAreUsed(options);
 
         if (options.apiModelingOptions().reportUnknownApiReferences) {
@@ -482,6 +486,8 @@
           classMergingEnqueuerExtensionBuilder.build(appView.graphLens());
       classMergingEnqueuerExtensionBuilder = null;
 
+      assert ArtProfileCompletenessChecker.verify(appView);
+
       if (!isKotlinLibraryCompilationWithInlinePassThrough
           && options.getProguardConfiguration().isOptimizing()) {
         if (options.enableVerticalClassMerging) {
@@ -502,6 +508,8 @@
         }
         assert appView.verticallyMergedClasses() != null;
 
+        assert ArtProfileCompletenessChecker.verify(appView);
+
         HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
             .runIfNecessary(executorService, timing, runtimeTypeCheckInfo);
       }
@@ -524,6 +532,8 @@
       // TODO: we should avoid removing liveness.
       Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
 
+      assert ArtProfileCompletenessChecker.verify(appView);
+
       timing.begin("Create IR");
       CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
       try {
@@ -535,6 +545,8 @@
         timing.end();
       }
 
+      assert ArtProfileCompletenessChecker.verify(appView);
+
       // Clear the reference type lattice element cache to reduce memory pressure.
       appView.dexItemFactory().clearTypeElementsCache();
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 6a0652a..b5fe77a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -199,7 +199,7 @@
   public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
     return new AppView<>(
         appInfo,
-        ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+        ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
         WholeProgramOptimizations.OFF,
         defaultTypeRewriter(appInfo));
   }
@@ -216,7 +216,7 @@
       T appInfo, TypeRewriter mapper, Timing timing) {
     return new AppView<>(
         appInfo,
-        ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+        ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
         WholeProgramOptimizations.OFF,
         mapper,
         timing);
@@ -240,7 +240,7 @@
             startupOrder);
     return new AppView<>(
         appInfo,
-        ArtProfileCollection.createInitialArtProfileCollection(application.options),
+        ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
         WholeProgramOptimizations.ON,
         defaultTypeRewriter(appInfo));
   }
@@ -248,7 +248,7 @@
   public static <T extends AppInfo> AppView<T> createForL8(T appInfo, TypeRewriter mapper) {
     return new AppView<>(
         appInfo,
-        ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+        ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
         WholeProgramOptimizations.OFF,
         mapper);
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 8050e50..b84809e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
@@ -91,6 +92,8 @@
               : IRCodeProvider.createThrowing();
       run(runtimeTypeCheckInfo, codeProvider, executorService, timing);
 
+      assert ArtProfileCompletenessChecker.verify(appView);
+
       // Clear type elements cache after IR building.
       appView.dexItemFactory().clearTypeElementsCache();
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index dbd19bd..22a544e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1484,7 +1484,8 @@
     // type.
     if (castType.isClassType()
         && castType.isAlwaysNull(appViewWithLiveness)
-        && !outValue.hasDebugUsers()) {
+        && !outValue.hasDebugUsers()
+        && options.testing.enableUtilityMethodsForCodeOptimizations) {
       // Replace all usages of the out-value by null.
       it.previous();
       Value nullValue = it.insertConstNullInstruction(code, options);
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
index 2cdb3e0..e7012b8 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -4,11 +4,14 @@
 
 package com.android.tools.r8.profile.art;
 
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -16,17 +19,15 @@
 
 public abstract class ArtProfileCollection {
 
-  public static ArtProfileCollection createInitialArtProfileCollection(InternalOptions options) {
+  public static ArtProfileCollection createInitialArtProfileCollection(
+      AppInfo appInfo, InternalOptions options) {
     ArtProfileOptions artProfileOptions = options.getArtProfileOptions();
     Collection<ArtProfileForRewriting> artProfilesForRewriting =
         artProfileOptions.getArtProfilesForRewriting();
-    if (artProfilesForRewriting.isEmpty()) {
-      return empty();
-    }
-    if (artProfileOptions.isPassthrough()) {
-      return passthrough();
-    }
-    List<ArtProfile> artProfiles = new ArrayList<>(artProfilesForRewriting.size());
+    List<ArtProfile> artProfiles =
+        new ArrayList<>(
+            artProfilesForRewriting.size()
+                + BooleanUtils.intValue(artProfileOptions.isCompletenessCheckForTestingEnabled()));
     for (ArtProfileForRewriting artProfileForRewriting :
         options.getArtProfileOptions().getArtProfilesForRewriting()) {
       ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
@@ -35,17 +36,31 @@
       artProfileForRewriting.getArtProfileProvider().getArtProfile(artProfileBuilder);
       artProfiles.add(artProfileBuilder.build());
     }
+    if (artProfileOptions.isCompletenessCheckForTestingEnabled()) {
+      artProfiles.add(createCompleteArtProfile(appInfo));
+    }
+    if (artProfiles.isEmpty()) {
+      return empty();
+    }
     return new NonEmptyArtProfileCollection(artProfiles);
   }
 
+  private static ArtProfile createCompleteArtProfile(AppInfo appInfo) {
+    ArtProfile.Builder artProfileBuilder = ArtProfile.builder();
+    for (DexProgramClass clazz : appInfo.classesWithDeterministicOrder()) {
+      artProfileBuilder.addRule(ArtProfileClassRule.builder().setType(clazz.getType()).build());
+      clazz.forEachMethod(
+          method ->
+              artProfileBuilder.addRule(
+                  ArtProfileMethodRule.builder().setMethod(method.getReference()).build()));
+    }
+    return artProfileBuilder.build();
+  }
+
   public static EmptyArtProfileCollection empty() {
     return EmptyArtProfileCollection.getInstance();
   }
 
-  public static PassthroughArtProfileCollection passthrough() {
-    return PassthroughArtProfileCollection.getInstance();
-  }
-
   public abstract boolean isNonEmpty();
 
   public abstract NonEmptyArtProfileCollection asNonEmpty();
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java
new file mode 100644
index 0000000..581db1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, 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.profile.art;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArtProfileCompletenessChecker {
+
+  public static boolean verify(AppView<?> appView) {
+    if (appView.options().getArtProfileOptions().isCompletenessCheckForTestingEnabled()) {
+      ArtProfile completeArtProfile = appView.getArtProfileCollection().asNonEmpty().getLast();
+      assert verifyProfileIsComplete(appView, completeArtProfile);
+    }
+    return true;
+  }
+
+  private static boolean verifyProfileIsComplete(AppView<?> appView, ArtProfile artProfile) {
+    assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+    List<DexReference> missing = new ArrayList<>();
+    for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+      if (appView.horizontallyMergedClasses().hasBeenMergedIntoDifferentType(clazz.getType())
+          || (appView.hasVerticallyMergedClasses()
+              && appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(clazz.getType()))) {
+        continue;
+      }
+      if (!artProfile.containsClassRule(clazz.getType())) {
+        missing.add(clazz.getType());
+      }
+      for (DexEncodedMethod method : clazz.methods()) {
+        if (!artProfile.containsMethodRule(method.getReference())) {
+          missing.add(method.getReference());
+        }
+      }
+    }
+    if (!missing.isEmpty()) {
+      String message =
+          StringUtils.join(System.lineSeparator(), missing, DexReference::toSmaliString);
+      assert false : message;
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
index 231a2d6..42e11f7 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -4,13 +4,17 @@
 
 package com.android.tools.r8.profile.art;
 
+import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyOrDefault;
+
 import java.util.Collection;
 import java.util.Collections;
 
 public class ArtProfileOptions {
 
   private Collection<ArtProfileForRewriting> artProfilesForRewriting = Collections.emptyList();
-  private boolean passthrough;
+  private boolean enableCompletenessCheckForTesting =
+      parseSystemPropertyOrDefault(
+          "com.android.tools.r8.artprofilerewritingcompletenesscheck", false);
 
   public ArtProfileOptions() {}
 
@@ -18,17 +22,18 @@
     return artProfilesForRewriting;
   }
 
+  public boolean isCompletenessCheckForTestingEnabled() {
+    return enableCompletenessCheckForTesting;
+  }
+
   public ArtProfileOptions setArtProfilesForRewriting(Collection<ArtProfileForRewriting> inputs) {
     this.artProfilesForRewriting = inputs;
     return this;
   }
 
-  public boolean isPassthrough() {
-    return passthrough;
-  }
-
-  public ArtProfileOptions setPassthrough(boolean passthrough) {
-    this.passthrough = passthrough;
+  public ArtProfileOptions setEnableCompletenessCheckForTesting(
+      boolean enableCompletenessCheckForTesting) {
+    this.enableCompletenessCheckForTesting = enableCompletenessCheckForTesting;
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
index b196906..d7f72ef 100644
--- a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -10,17 +10,19 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.function.Function;
 
 public class NonEmptyArtProfileCollection extends ArtProfileCollection
     implements Iterable<ArtProfile> {
 
-  private final Collection<ArtProfile> artProfiles;
+  private final List<ArtProfile> artProfiles;
 
-  public NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
+  public NonEmptyArtProfileCollection(List<ArtProfile> artProfiles) {
     this.artProfiles = artProfiles;
   }
 
@@ -34,6 +36,10 @@
     return this;
   }
 
+  public ArtProfile getLast() {
+    return ListUtils.last(artProfiles);
+  }
+
   @Override
   public Iterator<ArtProfile> iterator() {
     return artProfiles.iterator();
@@ -53,6 +59,14 @@
 
   @Override
   public void supplyConsumers(AppView<?> appView) {
+    if (appView.options().getArtProfileOptions().isCompletenessCheckForTestingEnabled()) {
+      assert ArtProfileCompletenessChecker.verify(appView);
+      ListUtils.removeLast(artProfiles);
+      if (artProfiles.isEmpty()) {
+        appView.setArtProfileCollection(ArtProfileCollection.empty());
+        return;
+      }
+    }
     NonEmptyArtProfileCollection collection =
         appView.getNamingLens().isIdentityLens()
             ? this
@@ -75,11 +89,10 @@
   }
 
   private NonEmptyArtProfileCollection map(Function<ArtProfile, ArtProfile> fn) {
-    ImmutableList.Builder<ArtProfile> newArtProfiles =
-        ImmutableList.builderWithExpectedSize(artProfiles.size());
+    List<ArtProfile> newArtProfiles = new ArrayList<>(artProfiles.size());
     for (ArtProfile artProfile : artProfiles) {
       newArtProfiles.add(fn.apply(artProfile));
     }
-    return new NonEmptyArtProfileCollection(newArtProfiles.build());
+    return new NonEmptyArtProfileCollection(newArtProfiles);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
deleted file mode 100644
index ac92f64..0000000
--- a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2022, 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.profile.art;
-
-import com.android.tools.r8.TextInputStream;
-import com.android.tools.r8.TextOutputStream;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileClassRule;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileMethodRule;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.UncheckedIOException;
-import java.util.function.Consumer;
-
-public class PassthroughArtProfileCollection extends ArtProfileCollection {
-
-  private static final PassthroughArtProfileCollection INSTANCE =
-      new PassthroughArtProfileCollection();
-
-  private PassthroughArtProfileCollection() {}
-
-  static PassthroughArtProfileCollection getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public boolean isNonEmpty() {
-    return false;
-  }
-
-  @Override
-  public NonEmptyArtProfileCollection asNonEmpty() {
-    return null;
-  }
-
-  @Override
-  public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
-    return this;
-  }
-
-  @Override
-  public ArtProfileCollection rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
-    return this;
-  }
-
-  @Override
-  public void supplyConsumers(AppView<?> appView) {
-    for (ArtProfileForRewriting artProfileForRewriting :
-        appView.options().getArtProfileOptions().getArtProfilesForRewriting()) {
-      ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
-      ArtProfileConsumer artProfileConsumer =
-          EmptyArtProfileConsumer.orEmpty(artProfileForRewriting.getResidualArtProfileConsumer());
-      supplyArtProfileConsumer(appView, artProfileConsumer, artProfileProvider);
-      artProfileConsumer.finished(appView.reporter());
-    }
-  }
-
-  private void supplyArtProfileConsumer(
-      AppView<?> appView,
-      ArtProfileConsumer artProfileConsumer,
-      ArtProfileProvider artProfileProvider) {
-    ArtProfileConsumerSupplier artProfileConsumerSupplier =
-        new ArtProfileConsumerSupplier(artProfileConsumer);
-    try {
-      ArtProfileRuleConsumer ruleConsumer =
-          EmptyArtProfileRuleConsumer.orEmpty(artProfileConsumer.getRuleConsumer());
-      artProfileProvider.getArtProfile(
-          new ArtProfileBuilder() {
-
-            @Override
-            public ArtProfileBuilder addClassRule(
-                Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
-              MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
-              classRuleBuilderConsumer.accept(classRule);
-              ruleConsumer.acceptClassRule(
-                  classRule.getClassReference(), classRule.getClassRuleInfo());
-              artProfileConsumerSupplier.supply(classRule);
-              return this;
-            }
-
-            @Override
-            public ArtProfileBuilder addMethodRule(
-                Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
-              MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
-              methodRuleBuilderConsumer.accept(methodRule);
-              ruleConsumer.acceptMethodRule(
-                  methodRule.getMethodReference(), methodRule.getMethodRuleInfo());
-              artProfileConsumerSupplier.supply(methodRule);
-              return this;
-            }
-
-            @Override
-            public ArtProfileBuilder addHumanReadableArtProfile(
-                TextInputStream textInputStream,
-                Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
-              HumanReadableArtProfileParser.Builder parserBuilder =
-                  HumanReadableArtProfileParser.builder()
-                      .setReporter(appView.reporter())
-                      .setProfileBuilder(this);
-              parserBuilderConsumer.accept(parserBuilder);
-              HumanReadableArtProfileParser parser = parserBuilder.build();
-              parser.parse(textInputStream, artProfileProvider.getOrigin());
-              return this;
-            }
-          });
-    } finally {
-      artProfileConsumerSupplier.close();
-    }
-  }
-
-  @Override
-  public ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
-    return this;
-  }
-
-  private static class ArtProfileConsumerSupplier {
-
-    private final OutputStreamWriter outputStreamWriter;
-
-    ArtProfileConsumerSupplier(ArtProfileConsumer artProfileConsumer) {
-      TextOutputStream textOutputStream = artProfileConsumer.getHumanReadableArtProfileConsumer();
-      this.outputStreamWriter =
-          textOutputStream != null
-              ? new OutputStreamWriter(
-                  textOutputStream.getOutputStream(), textOutputStream.getCharset())
-              : null;
-      ;
-    }
-
-    void supply(MutableArtProfileClassRule classRule) {
-      if (outputStreamWriter != null) {
-        try {
-          classRule.writeHumanReadableRuleString(outputStreamWriter);
-          outputStreamWriter.write('\n');
-        } catch (IOException e) {
-          throw new UncheckedIOException(e);
-        }
-      }
-    }
-
-    void supply(MutableArtProfileMethodRule methodRule) {
-      if (outputStreamWriter != null) {
-        try {
-          methodRule.writeHumanReadableRuleString(outputStreamWriter);
-          outputStreamWriter.write('\n');
-        } catch (IOException e) {
-          throw new UncheckedIOException(e);
-        }
-      }
-    }
-
-    void close() {
-      if (outputStreamWriter != null) {
-        try {
-          outputStreamWriter.close();
-        } catch (IOException e) {
-          throw new UncheckedIOException(e);
-        }
-      }
-    }
-  }
-}
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 2c06cad..f91b466 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2095,6 +2095,7 @@
     public boolean enableSwitchToIfRewriting = true;
     public boolean enableEnumUnboxingDebugLogs =
         System.getProperty("com.android.tools.r8.enableEnumUnboxingDebugLogs") != null;
+    public boolean enableUtilityMethodsForCodeOptimizations = true;
     public boolean forceRedundantConstNumberRemoval = false;
     public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
     public boolean invertConditionals = false;
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index eef3dcf..26aa1b1 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -48,6 +48,17 @@
   public void testR8() throws Exception {
     testForR8(Backend.DEX)
         .addProgramFiles(outDirectory.resolve("program.jar"))
+        .addOptionsModification(options -> options.enableEnumUnboxing = false)
+        .addOptionsModification(options -> options.outline.enabled = false)
+        .addOptionsModification(
+            options -> options.apiModelingOptions().enableOutliningOfMethods = false)
+        .addOptionsModification(
+            options -> options.apiModelingOptions().enableStubbingOfClasses = false)
+        .addOptionsModification(options -> options.callSiteOptimizationOptions().setEnabled(false))
+        .addOptionsModification(
+            options -> options.testing.enableUtilityMethodsForCodeOptimizations = false)
+        .addOptionsModification(
+            options -> options.getArtProfileOptions().setEnableCompletenessCheckForTesting(true))
         .apply(this::configure)
         .compile();
   }
diff --git a/tools/test.py b/tools/test.py
index a4e7b6e..e7e2b99 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -78,6 +78,10 @@
   result.add_option('--all-tests', '--all_tests',
       help='Run tests in all configurations.',
       default=False, action='store_true')
+  result.add_option('--art-profile-rewriting-completeness-check',
+       '--art_profile_rewriting_completeness_check',
+      help='Enable completeness check for ART profile rewriting.',
+      default=False, action='store_true')
   result.add_option('--slow-tests', '--slow_tests',
       help='Also run slow tests.',
       default=False, action='store_true')
@@ -287,6 +291,8 @@
     gradle_args.append('-Ponly_internal')
   if options.all_tests:
     gradle_args.append('-Pall_tests')
+  if options.art_profile_rewriting_completeness_check:
+    gradle_args.append('-Part_profile_rewriting_completeness_check=1')
   if options.slow_tests:
     gradle_args.append('-Pslow_tests=1')
   if options.tool: