Test partial record desugaring

Bug: b/357021427
Change-Id: I067ed42a3cc0235f29c714731410b32487d0a8e6
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index e4aa9dc..3c858a9 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -877,7 +877,7 @@
               clazz.getPermittedSubclassAttributes(), options.itemFactory));
     }
 
-    if (clazz.isRecord() && options.canUseRecords()) {
+    if (clazz.isRecord() && options.emitRecordAnnotationsInDex) {
       annotations.add(DexAnnotation.createRecordAnnotation(clazz, appView));
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
index 63636d9..0b301d4 100644
--- a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
+++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
@@ -16,7 +16,7 @@
 
   public static ApplicationReaderMap getInstance(InternalOptions options) {
     ApplicationReaderMap result = new EmptyMap();
-    if (options.desugarRecordState().isNotOff()
+    if (options.desugarRecordState().isFull()
         && !options.testing.disableRecordApplicationReaderMap) {
       result = new RecordMap(options.dexItemFactory());
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
index 268c8a3..fb6b45b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
@@ -119,7 +119,6 @@
     throw new Unreachable("Invoke dynamic needs record desugaring but could not be desugared.");
   }
 
-
   @Override
   @SuppressWarnings("ReferenceEquality")
   public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
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 4e50c82..cd63846 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -510,8 +510,12 @@
 
   // TODO(b/293591931): Remove this flag when records are stable in Platform
   //  Flag to allow record annotations in DEX. See b/231930852 for context.
-  private final boolean emitRecordAnnotationsInDex =
+  public boolean emitRecordAnnotationsInDex =
       System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
+  // This flag to allows platform to disable partial desugaring, so when the annotation is set
+  // platform can use the invoke-dynamic for records.
+  public boolean recordPartialDesugaring =
+      System.getProperty("com.android.tools.r8.disableRecordPartialDesugaring") == null;
 
   // Flag to allow nest annotations in DEX. See b/231930852 for context.
   public boolean emitNestAnnotationsInDex =
@@ -713,17 +717,15 @@
     }
   }
 
-  public boolean recordPartialDesugaring =
-      System.getProperty("com.android.tools.r8.recordPartialDesugaring") != null;
-
   public DesugarRecordState desugarRecordState() {
     if (desugarState.isOff()) {
       return DesugarRecordState.OFF;
     }
-    if (!canUseRecords()) {
-      return DesugarRecordState.FULL;
+    // The class java.lang.Record is present from U with a GC issue so we enable from V.
+    if (hasFeaturePresentFrom(AndroidApiLevel.V) && recordPartialDesugaring) {
+      return DesugarRecordState.PARTIAL;
     }
-    return recordPartialDesugaring ? DesugarRecordState.PARTIAL : DesugarRecordState.OFF;
+    return emitRecordAnnotationsInDex ? DesugarRecordState.OFF : DesugarRecordState.FULL;
   }
 
   public boolean canUseDesugarBufferCovariantReturnType() {
@@ -2739,10 +2741,6 @@
     return (hasFeaturePresentFrom(null) || emitNestAnnotationsInDex) && !forceNestDesugaring;
   }
 
-  public boolean canUseRecords() {
-    return hasFeaturePresentFrom(null) || emitRecordAnnotationsInDex;
-  }
-
   public boolean canUseSealedClasses() {
     return hasFeaturePresentFrom(AndroidApiLevel.U) || emitPermittedSubclassesAnnotationsInDex;
   }
diff --git a/src/test/examplesJava17/records/RecordHashCodeTest.java b/src/test/examplesJava17/records/RecordHashCodeTest.java
index 59d4666..2f2609d 100644
--- a/src/test/examplesJava17/records/RecordHashCodeTest.java
+++ b/src/test/examplesJava17/records/RecordHashCodeTest.java
@@ -57,7 +57,7 @@
         .setMinApi(parameters)
         .run(parameters.getRuntime(), TestClass.class)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
index 5c08537..84f3ea9 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordAnnotationTest.java
@@ -20,7 +20,7 @@
   private static final String RECORD_NAME = "EmptyRecordAnnotation";
   private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
   private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
-  private static final String EXPECTED_RESULT_NATIVE_RECORD =
+  private static final String EXPECTED_RESULT_NATIVE_OR_PARTIALLY_DESUGARED_RECORD =
       StringUtils.lines("class java.lang.Record", "class records.EmptyRecordAnnotation$Empty");
   private static final String EXPECTED_RESULT_DESUGARED_RECORD =
       StringUtils.lines(
@@ -47,7 +47,7 @@
     testForJvm(parameters)
         .addProgramClassFileData(PROGRAM_DATA)
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD);
+        .assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_OR_PARTIALLY_DESUGARED_RECORD);
   }
 
   @Test
@@ -59,9 +59,9 @@
         .compile()
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForD8(parameters),
+            isRecordsFullyDesugaredForD8(parameters),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_RECORD),
-            r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD));
+            r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_OR_PARTIALLY_DESUGARED_RECORD));
   }
 
   @Test
@@ -77,14 +77,14 @@
         .addKeepMainRule(MAIN_TYPE)
         // This is used to avoid renaming com.android.tools.r8.RecordTag.
         .applyIf(
-            isRecordsDesugaredForR8(parameters),
+            isRecordsFullyDesugaredForR8(parameters),
             b -> b.addKeepRules("-keep class java.lang.Record"))
         .compile()
         .applyIf(parameters.isCfRuntime(), r -> r.inspect(RecordTestUtils::assertRecordsAreRecords))
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForR8(parameters),
+            isRecordsFullyDesugaredForR8(parameters),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_RECORD),
-            r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_RECORD));
+            r -> r.assertSuccessWithOutput(EXPECTED_RESULT_NATIVE_OR_PARTIALLY_DESUGARED_RECORD));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
index 05fdb8d..10f10f4 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
@@ -83,7 +83,7 @@
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT)),
             r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
index d2c05af..41f97f8 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentAnnotationsTest.java
@@ -202,6 +202,9 @@
   @Parameter(1)
   public Boolean keepAnnotations;
 
+  // Enable once records are no longer partially desugared on platform.
+  public boolean recordDesugaringIsOffOnDex = false;
+
   @Parameters(name = "{0}, keepAnnotations: {1}")
   public static List<Object[]> data() {
     return buildParameters(
@@ -234,7 +237,7 @@
         .addProgramClassFileData(PROGRAM_DATA)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForD8(parameters),
+            parameters.isDexRuntime(),
             r ->
                 r.assertSuccessWithOutput(
                     runtimeWithRecordsSupport(parameters.getRuntime())
@@ -306,7 +309,7 @@
     parameters.assumeR8TestParameters();
     testForR8(parameters.getBackend())
         .addProgramClassFileData(PROGRAM_DATA)
-        // TODO(b/231930852): Change to android.jar for Androud U when that contains
+        // TODO(b/231930852): Change to android.jar for Android U when that contains
         // java.lang.Record.
         .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
         .addKeepMainRule(MAIN_TYPE)
@@ -323,7 +326,7 @@
               ClassSubject person = inspector.clazz("records.RecordWithAnnotations$Person");
               FieldSubject name = person.uniqueFieldWithOriginalName("name");
               FieldSubject age = person.uniqueFieldWithOriginalName("age");
-              if (!isRecordsDesugaredForR8(parameters)) {
+              if (parameters.isCfRuntime()) {
                 assertEquals(2, person.getFinalRecordComponents().size());
 
                 assertEquals(
@@ -376,7 +379,7 @@
                     keepAnnotations
                         ? JVM_EXPECTED_RESULT_R8
                         : JVM_EXPECTED_RESULT_R8_NO_KEEP_ANNOTATIONS),
-            !isRecordsDesugaredForR8(parameters),
+            recordDesugaringIsOffOnDex,
             r ->
                 r.assertSuccessWithOutput(
                     keepAnnotations
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
index 58f0da0..27e7e0b 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordComponentSignatureTest.java
@@ -88,8 +88,7 @@
         .addProgramClassFileData(PROGRAM_DATA)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            !isRecordsDesugaredForD8(parameters),
-            // Current Art 14 build does not support the java.lang.Record class.
+            parameters.isCfRuntime(),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT),
             r ->
                 r.assertSuccessWithOutput(
@@ -100,7 +99,7 @@
                         inspector -> {
                           ClassSubject person =
                               inspector.clazz("records.RecordWithSignature$Person");
-                          if (!isRecordsDesugaredForD8(parameters)) {
+                          if (parameters.isCfRuntime()) {
                             assertEquals(2, person.getFinalRecordComponents().size());
 
                             assertEquals(
@@ -159,7 +158,7 @@
             runtimeWithRecordsSupport(parameters.getRuntime()),
             r ->
                 r.assertSuccessWithOutput(
-                    isRecordsDesugaredForR8(parameters)
+                    parameters.isDexRuntime()
                         ? EXPECTED_RESULT_DESUGARED_NATIVE_RECORD_SUPPORT
                         : EXPECTED_RESULT_R8),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_DESUGARED_NO_NATIVE_RECORDS_SUPPORT));
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
index 8eb35d5..169ef16 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInterfaceTest.java
@@ -66,7 +66,7 @@
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
@@ -81,7 +81,7 @@
     testForD8()
         .addProgramFiles(path)
         .applyIf(
-            isRecordsDesugaredForD8(parameters),
+            isRecordsFullyDesugaredForD8(parameters),
             b ->
                 b.getBuilder()
                     .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()),
@@ -105,7 +105,7 @@
     testForD8()
         .addProgramFiles(path)
         .applyIf(
-            isRecordsDesugaredForD8(parameters),
+            isRecordsFullyDesugaredForD8(parameters),
             b ->
                 b.getBuilder()
                     .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()),
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
index fa22262..b782568 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Path;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,11 +45,9 @@
           "true",
           "false",
           "false",
-          "%s[%s=Jane Doe, %s=42]");
+          "%s[name=Jane Doe, age=42]");
   private static final String EXPECTED_RESULT_D8 =
-      String.format(EXPECTED_RESULT, "Empty", "Person", "name", "age");
-  private static final String EXPECTED_RESULT_R8 =
-      String.format(EXPECTED_RESULT, "a", "b", "name", "age");
+      String.format(EXPECTED_RESULT, "Empty", "Person");
 
   private final TestParameters parameters;
 
@@ -86,11 +85,12 @@
             .setMinApi(parameters)
             .compile()
             .writeToZip();
-    if (isRecordsDesugaredForD8(parameters)) {
+    if (isRecordsFullyDesugaredForR8(parameters)) {
       assertTrue(ZipUtils.containsEntry(desugared, "com/android/tools/r8/RecordTag.class"));
     } else {
       assertFalse(ZipUtils.containsEntry(desugared, "com/android/tools/r8/RecordTag.class"));
     }
+    String[] minifiedNames = {null, null};
     testForR8(parameters.getBackend())
         .addProgramFiles(desugared)
         .setMinApi(parameters)
@@ -98,9 +98,10 @@
         .allowDiagnosticMessages()
         .compileWithExpectedDiagnostics(
             // Class com.android.tools.r8.RecordTag in desugared input is seen as java.lang.Record
-            // when reading causing the duplicate class.
+            // when reading causing the duplicate class. From Android V the issue is solved by
+            // partial desugaring.
             diagnostics -> {
-              if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.U)) {
+              if (parameters.getApiLevel().isEqualTo(AndroidApiLevel.U)) {
                 diagnostics
                     .assertNoErrors()
                     .assertInfosMatch(
@@ -116,7 +117,18 @@
                 diagnostics.assertNoMessages();
               }
             })
+        .inspect(
+            i -> {
+              minifiedNames[0] = extractSimpleFinalName(i, "records.RecordInvokeCustom$Empty");
+              minifiedNames[1] = extractSimpleFinalName(i, "records.RecordInvokeCustom$Person");
+            })
         .run(parameters.getRuntime(), MAIN_TYPE)
-        .assertSuccessWithOutput(EXPECTED_RESULT_R8);
+        .assertSuccessWithOutput(
+            String.format(EXPECTED_RESULT, minifiedNames[0], minifiedNames[1]));
+  }
+
+  private static String extractSimpleFinalName(CodeInspector i, String name) {
+    String finalName = i.clazz(name).getFinalName();
+    return finalName.split("\\.")[1];
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
index a46c400..d05a57a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -74,7 +74,7 @@
             .addProgramClassFileData(PROGRAM_DATA_1)
             .setMinApi(parameters)
             .setIntermediate(true);
-    if (isRecordsDesugaredForD8(parameters)) {
+    if (isRecordsFullyDesugaredForD8(parameters)) {
       assertThrows(
           CompilationFailedException.class,
           () ->
@@ -129,8 +129,8 @@
             .inspect(this::assertDoesNotHaveRecordTag)
             .writeToZip();
 
-    assertTrue(isRecordsDesugaredForD8(parameters) ^ !globals1.hasGlobals());
-    assertTrue(isRecordsDesugaredForD8(parameters) ^ !globals2.hasGlobals());
+    assertTrue(isRecordsFullyDesugaredForD8(parameters) ^ !globals1.hasGlobals());
+    assertTrue(isRecordsFullyDesugaredForD8(parameters) ^ !globals2.hasGlobals());
 
     D8TestCompileResult result =
         testForD8(parameters.getBackend())
@@ -147,14 +147,14 @@
     result
         .run(parameters.getRuntime(), MAIN_TYPE_1)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
     result
         .run(parameters.getRuntime(), MAIN_TYPE_2)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
@@ -183,14 +183,14 @@
     result
         .run(parameters.getRuntime(), MAIN_TYPE_1)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
     result
         .run(parameters.getRuntime(), MAIN_TYPE_2)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
@@ -214,7 +214,7 @@
             .inspect(this::assertHasRecordTag)
             .writeToZip();
 
-    if (!isRecordsDesugaredForD8(parameters)) {
+    if (!isRecordsFullyDesugaredForD8(parameters)) {
       D8TestCompileResult result =
           testForD8(parameters.getBackend())
               .addProgramFiles(output1, output2)
@@ -223,14 +223,14 @@
       result
           .run(parameters.getRuntime(), MAIN_TYPE_1)
           .applyIf(
-              isRecordsDesugaredForD8(parameters)
+              isRecordsFullyDesugaredForD8(parameters)
                   || runtimeWithRecordsSupport(parameters.getRuntime()),
               r -> r.assertSuccessWithOutput(EXPECTED_RESULT_1),
               r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
       result
           .run(parameters.getRuntime(), MAIN_TYPE_2)
           .applyIf(
-              isRecordsDesugaredForD8(parameters)
+              isRecordsFullyDesugaredForD8(parameters)
                   || runtimeWithRecordsSupport(parameters.getRuntime()),
               r -> r.assertSuccessWithOutput(EXPECTED_RESULT_2),
               r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
@@ -252,7 +252,7 @@
   private void assertHasRecordTag(CodeInspector inspector) {
     // Note: this should be asserting on record tag.
     assertThat(
-        inspector.clazz("java.lang.Record"), isPresentIf(isRecordsDesugaredForD8(parameters)));
+        inspector.clazz("java.lang.Record"), isPresentIf(isRecordsFullyDesugaredForD8(parameters)));
   }
 
   private void assertDoesNotHaveRecordTag(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
index 35725b8..1194f76 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -4,12 +4,13 @@
 
 package com.android.tools.r8.desugar.records;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.LibraryFilesHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -89,7 +90,11 @@
     }
   }
 
-  public static void assertNoJavaLangRecord(CodeInspector inspector) {
-    assertThat(inspector.clazz("java.lang.Record"), isAbsent());
+  public static void assertNoJavaLangRecord(CodeInspector inspector, TestParameters parameters) {
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.V)) {
+      assertFalse(inspector.clazz("java.lang.RecordTag").isPresent());
+    } else {
+      assertFalse(inspector.clazz("java.lang.Record").isPresent());
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index 31cf89a..9dbd8d2 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.desugar.records;
 
+import static com.android.tools.r8.desugar.records.RecordTestUtils.assertNoJavaLangRecord;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
@@ -81,11 +82,11 @@
         .setMinApi(parameters)
         .compile()
         .inspectWithOptions(
-            RecordTestUtils::assertNoJavaLangRecord,
+            i -> assertNoJavaLangRecord(i, parameters),
             options -> options.testing.disableRecordApplicationReaderMap = true)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT),
             r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
@@ -100,7 +101,8 @@
     Path path = compileIntermediate(globals);
     testForD8()
         .addProgramFiles(path)
-        .apply(
+        .applyIf(
+            isRecordsFullyDesugaredForD8(parameters),
             b ->
                 b.getBuilder()
                     .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
@@ -119,7 +121,8 @@
     // In Android Studio they disable desugaring at this point to improve build speed.
     testForD8()
         .addProgramFiles(path)
-        .apply(
+        .applyIf(
+            isRecordsFullyDesugaredForD8(parameters),
             b ->
                 b.getBuilder()
                     .addGlobalSyntheticsResourceProviders(globals.getIndexedModeProvider()))
@@ -161,10 +164,7 @@
           .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
           .compile()
           .inspect(RecordTestUtils::assertRecordsAreRecords)
-          .inspect(
-              inspector -> {
-                inspector.clazz("records.SimpleRecord$Person").isRenamed();
-              })
+          .inspect(inspector -> inspector.clazz("records.SimpleRecord$Person").isRenamed())
           .run(parameters.getRuntime(), MAIN_TYPE)
           .assertSuccessWithOutput(EXPECTED_RESULT);
       return;
@@ -172,7 +172,7 @@
     builder
         .compile()
         .inspectWithOptions(
-            RecordTestUtils::assertNoJavaLangRecord,
+            i -> assertNoJavaLangRecord(i, parameters),
             options -> options.testing.disableRecordApplicationReaderMap = true)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .assertSuccessWithOutput(EXPECTED_RESULT);
@@ -201,7 +201,7 @@
     builder
         .compile()
         .inspectWithOptions(
-            RecordTestUtils::assertNoJavaLangRecord,
+            i -> assertNoJavaLangRecord(i, parameters),
             options -> options.testing.disableRecordApplicationReaderMap = true)
         .run(parameters.getRuntime(), MAIN_TYPE)
         .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 448acc4..602cdfc 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -102,7 +102,7 @@
                     options -> options.testing.disableRecordApplicationReaderMap = true))
         .run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
         .applyIf(
-            isRecordsDesugaredForD8(parameters)
+            isRecordsFullyDesugaredForD8(parameters)
                 || runtimeWithRecordsSupport(parameters.getRuntime()),
             r -> r.assertSuccessWithOutput(EXPECTED_RESULT),
             r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
@@ -168,7 +168,8 @@
         false,
         false,
         parameters.canUseNestBasedAccessesWhenDesugaring(),
-        !isRecordsDesugaredForD8(parameters));
+        !isRecordsFullyDesugaredForD8(parameters),
+        false);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
@@ -179,7 +180,8 @@
         parameters.canHaveNonReboundConstructorInvoke(),
         true,
         parameters.canUseNestBasedAccesses(),
-        !isRecordsDesugaredForR8(parameters));
+        !isRecordsFullyDesugaredForR8(parameters),
+        parameters.isCfRuntime());
   }
 
   private void inspect(
@@ -189,7 +191,8 @@
       boolean canHaveNonReboundConstructorInvoke,
       boolean canMergeRecordTag,
       boolean canUseNestBasedAccesses,
-      boolean canUseRecords) {
+      boolean partialDesugaring,
+      boolean recordDesugaringIsOff) { // Record desugaring is partial or full.
     ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
     assertThat(mainClassSubject, isPresent());
 
@@ -197,7 +200,7 @@
     assertThat(mainMethodSubject, isPresent());
 
     ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
-    assertThat(recordTagClassSubject, isAbsentIf(canMergeRecordTag || canUseRecords));
+    assertThat(recordTagClassSubject, isAbsentIf(canMergeRecordTag || partialDesugaring));
     if (recordTagClassSubject.isPresent()) {
       assertEquals(1, recordTagClassSubject.allMethods().size());
     }
@@ -208,19 +211,20 @@
     ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
     assertThat(personRecordClassSubject, isPresent());
     assertEquals(
-        canUseRecords
+        partialDesugaring
             ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
             : canMergeRecordTag
                 ? inspector.getTypeSubject(Object.class.getTypeName())
                 : recordTagClassSubject.asTypeSubject(),
         personRecordClassSubject.getSuperType());
     assertEquals(
-        canUseRecords ? 6 : canMergeRecordTag ? 11 : 10,
+        recordDesugaringIsOff ? 6 : (canMergeRecordTag && !partialDesugaring) ? 11 : 10,
         personRecordClassSubject.allMethods().size());
 
     MethodSubject personDefaultInstanceInitializerSubject = personRecordClassSubject.init();
     assertThat(
-        personDefaultInstanceInitializerSubject, isPresentIf(canMergeRecordTag && !canUseRecords));
+        personDefaultInstanceInitializerSubject,
+        isPresentIf(canMergeRecordTag && !partialDesugaring));
 
     MethodSubject personInstanceInitializerSubject =
         canMergeRecordTag
@@ -253,51 +257,54 @@
     // boolean equals(Object)
     MethodSubject getFieldsAsObjectsMethodSubject =
         personRecordClassSubject.uniqueMethodWithOriginalName(GET_FIELDS_AS_OBJECTS_METHOD_NAME);
-    assertThat(getFieldsAsObjectsMethodSubject, isAbsentIf(canUseRecords));
+    assertThat(getFieldsAsObjectsMethodSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject equalsHelperMethodSubject =
         personRecordClassSubject.uniqueMethodWithOriginalName(EQUALS_RECORD_METHOD_NAME);
-    assertThat(equalsHelperMethodSubject, isAbsentIf(canUseRecords));
+    assertThat(equalsHelperMethodSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject equalsMethodSubject =
         personRecordClassSubject.uniqueMethodWithOriginalName("equals");
     assertThat(equalsMethodSubject, isPresent());
     assertThat(
-        equalsMethodSubject, ifThen(!canUseRecords, invokesMethod(equalsHelperMethodSubject)));
+        equalsMethodSubject,
+        ifThen(!recordDesugaringIsOff, invokesMethod(equalsHelperMethodSubject)));
 
     // int hashCode()
     ClassSubject hashCodeHelperClassSubject =
         inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1));
-    assertThat(hashCodeHelperClassSubject, isAbsentIf(canUseRecords));
+    assertThat(hashCodeHelperClassSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject hashCodeHelperMethodSubject = hashCodeHelperClassSubject.uniqueMethod();
-    assertThat(hashCodeHelperMethodSubject, isAbsentIf(canUseRecords));
+    assertThat(hashCodeHelperMethodSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject hashCodeMethodSubject =
         personRecordClassSubject.uniqueMethodWithOriginalName("hashCode");
     assertThat(hashCodeMethodSubject, isPresent());
     assertThat(
         hashCodeMethodSubject,
-        ifThen(!canUseRecords, invokesMethod(getFieldsAsObjectsMethodSubject)));
+        ifThen(!recordDesugaringIsOff, invokesMethod(getFieldsAsObjectsMethodSubject)));
     assertThat(
-        hashCodeMethodSubject, ifThen(!canUseRecords, invokesMethod(hashCodeHelperMethodSubject)));
+        hashCodeMethodSubject,
+        ifThen(!recordDesugaringIsOff, invokesMethod(hashCodeHelperMethodSubject)));
 
     // String toString()
     ClassSubject toStringHelperClassSubject =
         inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0));
-    assertThat(toStringHelperClassSubject, isAbsentIf(canUseRecords));
+    assertThat(toStringHelperClassSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject toStringHelperMethodSubject = toStringHelperClassSubject.uniqueMethod();
-    assertThat(toStringHelperMethodSubject, isAbsentIf(canUseRecords));
+    assertThat(toStringHelperMethodSubject, isAbsentIf(recordDesugaringIsOff));
 
     MethodSubject toStringMethodSubject =
         personRecordClassSubject.uniqueMethodWithOriginalName("toString");
     assertThat(toStringMethodSubject, isPresent());
     assertThat(
         toStringMethodSubject,
-        ifThen(!canUseRecords, invokesMethod(getFieldsAsObjectsMethodSubject)));
+        ifThen(!recordDesugaringIsOff, invokesMethod(getFieldsAsObjectsMethodSubject)));
     assertThat(
-        toStringMethodSubject, ifThen(!canUseRecords, invokesMethod(toStringHelperMethodSubject)));
+        toStringMethodSubject,
+        ifThen(!recordDesugaringIsOff, invokesMethod(toStringHelperMethodSubject)));
 
     profileInspector
         .assertContainsClassRules(mainClassSubject, personRecordClassSubject)
@@ -315,22 +322,22 @@
                 i.assertContainsMethodRules(
                     nameNestAccessorMethodSubject, ageNestAccessorMethodSubject))
         .applyIf(
-            canMergeRecordTag && !canUseRecords,
+            canMergeRecordTag && !partialDesugaring,
             i -> i.assertContainsMethodRule(personDefaultInstanceInitializerSubject))
         .applyIf(
-            !canUseRecords,
+            !canMergeRecordTag && !partialDesugaring,
+            j ->
+                j.assertContainsClassRules(recordTagClassSubject)
+                    .assertContainsMethodRule(recordTagInstanceInitializerSubject))
+        .applyIf(
+            !recordDesugaringIsOff,
             i ->
                 i.assertContainsClassRules(hashCodeHelperClassSubject, toStringHelperClassSubject)
                     .assertContainsMethodRules(
                         equalsHelperMethodSubject,
                         getFieldsAsObjectsMethodSubject,
                         hashCodeHelperMethodSubject,
-                        toStringHelperMethodSubject)
-                    .applyIf(
-                        !canMergeRecordTag,
-                        j ->
-                            j.assertContainsClassRules(recordTagClassSubject)
-                                .assertContainsMethodRule(recordTagInstanceInitializerSubject)))
+                        toStringHelperMethodSubject))
         .assertContainsNoOtherRules();
   }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index b4a809b..e3cc4c8 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -1761,19 +1761,14 @@
     throw new Unreachable();
   }
 
-  public static boolean isRecordsDesugaredForD8(TestParameters parameters) {
+  public static boolean isRecordsFullyDesugaredForD8(TestParameters parameters) {
     assert parameters.getApiLevel() != null;
-    // TODO(b/293591931): Return true for some API level when records are stable in Platform
-    //  (expecting Android V) using TestBase.apiLevelWithRecordSupport().
-    return true;
+    return parameters.getApiLevel().isLessThan(AndroidApiLevel.V);
   }
 
-  public static boolean isRecordsDesugaredForR8(TestParameters parameters) {
+  public static boolean isRecordsFullyDesugaredForR8(TestParameters parameters) {
     assert parameters.getApiLevel() != null;
-    // TODO(b/293591931): Also return true for some API level when records are stable in Platform
-    //  (expecting Android V) using TestBase.apiLevelWithRecordSupport(). Note that R8 with class
-    //  file output never performs desugaring.
-    return !parameters.getRuntime().isCf();
+    return !parameters.getRuntime().isCf() && isRecordsFullyDesugaredForD8(parameters);
   }
 
   public static boolean canUseJavaUtilObjects(TestParameters parameters) {