Reenable startup tests

Bug: b/271822426
Change-Id: I1a303235e6250468b8137cc799f506d3989c3c61
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
index e8be76e..b85dbaa 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -62,14 +62,7 @@
           missingItemsDiagnosticBuilderFactory) {
     StartupProfile startupProfile =
         StartupProfile.parseStartupProfile(options, missingItemsDiagnosticBuilderFactory);
-    if (startupProfile == null || startupProfile.isEmpty()) {
-      return empty();
-    }
-    StartupProfile.Builder builder = StartupProfile.builder();
-    for (StartupProfileRule startupItem : startupProfile.getRules()) {
-      builder.addStartupItem(startupItem);
-    }
-    return builder.build();
+    return startupProfile != null ? startupProfile : empty();
   }
 
   public static StartupProfile createInitialStartupProfileForD8(AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
index a5145f4..bb1bbc9 100644
--- a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
@@ -37,6 +37,10 @@
     return COMPARATOR;
   }
 
+  public static TypeReference getVoidType() {
+    return null;
+  }
+
   public static DexProto toDexProto(
       List<TypeReference> formalTypes, TypeReference returnType, DexItemFactory dexItemFactory) {
     return toDexProto(
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 703c916..92d3129 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -29,6 +30,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.TypeReferenceUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -36,11 +38,11 @@
 import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -50,6 +52,11 @@
 @RunWith(Parameterized.class)
 public class StartupSyntheticPlacementTest extends TestBase {
 
+  private enum Compiler {
+    D8,
+    R8
+  }
+
   @Parameter(0)
   public TestParameters parameters;
 
@@ -120,12 +127,11 @@
         .compile()
         .inspectMultiDex(
             r8CompileResult.writeProguardMap(), this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .apply(this::checkCompleteness)
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
         .assertSuccessWithOutputLines(getExpectedOutput());
   }
 
-  // TODO(b/271822426): Reenable test.
-  @Ignore("b/271822426")
   @Test
   public void testLayoutUsingR8() throws Exception {
     // First generate a startup list for the original app.
@@ -161,11 +167,17 @@
         .setMinApi(parameters)
         .compile()
         .inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .apply(this::checkCompleteness)
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
-        .applyIf(
-            enableStartupCompletenessCheck && useLambda,
-            runResult -> runResult.assertFailureWithErrorThatThrows(NullPointerException.class),
-            runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
+        .assertSuccessWithOutputLines(getExpectedOutput());
+  }
+
+  private void checkCompleteness(TestCompileResult<?, ?> compileResult) throws Exception {
+    if (enableStartupCompletenessCheck && !useLambda) {
+      compileResult
+          .run(parameters.getRuntime(), Main.class, "true")
+          .assertFailureWithErrorThatThrows(NullPointerException.class);
+    }
   }
 
   private void configureStartupOptions(
@@ -182,7 +194,7 @@
               options
                   .getTestingOptions()
                   .setMixedSectionLayoutStrategyInspector(
-                      getMixedSectionLayoutInspector(inspector));
+                      getMixedSectionLayoutInspector(inspector, testBuilder.isD8TestBuilder()));
             })
         .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
   }
@@ -212,12 +224,35 @@
                 Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
             .build());
     if (useLambda) {
+      builder.add(
+          ExternalStartupMethod.builder()
+              .setMethodReference(
+                  Reference.methodFromMethod(B.class.getDeclaredMethod("synthesize")))
+              .build());
       if (isStartupListForOriginalApp) {
-        // TODO(b/271822426): Update after rewriting startup profile.
-        /*builder.add(
-        ExternalSyntheticStartupMethod.builder()
-            .setSyntheticContextReference(Reference.classFromClass(B.class))
-            .build());*/
+        ClassReference syntheticLambdaClassReference = getSyntheticLambdaClassReference();
+        builder.add(
+            ExternalStartupClass.builder().setClassReference(syntheticLambdaClassReference).build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    MethodReferenceUtils.instanceConstructor(syntheticLambdaClassReference))
+                .build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    Reference.method(
+                        syntheticLambdaClassReference,
+                        "accept",
+                        Collections.singletonList(Reference.classFromClass(Object.class)),
+                        TypeReferenceUtils.getVoidType()))
+                .build(),
+            ExternalStartupMethod.builder()
+                .setMethodReference(
+                    Reference.method(
+                        Reference.classFromClass(B.class),
+                        "lambda$synthesize$0",
+                        Collections.singletonList(Reference.classFromClass(Object.class)),
+                        TypeReferenceUtils.getVoidType()))
+                .build());
       } else {
         ClassSubject bClassSubject = inspector.clazz(B.class);
 
@@ -264,7 +299,8 @@
       builder.add(
           ExternalStartupMethod.builder()
               .setMethodReference(
-                  Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
+                  Reference.methodFromMethod(
+                      B.class.getDeclaredMethod("lambda$synthesize$0", Object.class)))
               .build());
     }
     builder.add(
@@ -276,7 +312,7 @@
   }
 
   private List<ClassReference> getExpectedClassDataLayout(
-      CodeInspector inspector, int virtualFile) {
+      CodeInspector inspector, boolean isD8, int virtualFile) {
     ClassSubject syntheticLambdaClassSubject = inspector.clazz(getSyntheticLambdaClassReference());
 
     // The synthetic lambda should only be placed alongside its synthetic context (B) if it is used.
@@ -287,10 +323,17 @@
           Reference.classFromClass(Main.class),
           Reference.classFromClass(A.class),
           Reference.classFromClass(B.class));
-      if (useLambda) {
-        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+      if (isD8) {
+        if (useLambda) {
+          layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+        }
+        layoutBuilder.add(Reference.classFromClass(C.class));
+      } else {
+        layoutBuilder.add(Reference.classFromClass(C.class));
+        if (useLambda) {
+          layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
+        }
       }
-      layoutBuilder.add(Reference.classFromClass(C.class));
     }
     if (!useLambda) {
       if (!enableMinimalStartupDex || virtualFile == 1) {
@@ -300,12 +343,14 @@
     return layoutBuilder.build();
   }
 
-  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(CodeInspector inspector) {
+  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(
+      CodeInspector inspector, boolean isD8) {
     return new MixedSectionLayoutInspector() {
       @Override
       public void inspectClassDataLayout(int virtualFile, Collection<DexProgramClass> layout) {
         assertThat(
-            layout, isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, virtualFile)));
+            layout,
+            isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, isD8, virtualFile)));
       }
     };
   }
@@ -355,11 +400,15 @@
     static void b(boolean useLambda) {
       String message = System.currentTimeMillis() > 0 ? "B" : null;
       if (useLambda) {
-        Consumer<Object> consumer = obj -> {};
-        consumer.accept(consumer);
+        synthesize();
       }
       System.out.println(message);
     }
+
+    static void synthesize() {
+      Consumer<Object> consumer = obj -> {};
+      consumer.accept(consumer);
+    }
   }
 
   static class C {
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 9bde49b..14a1e1b 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -26,14 +26,15 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.TypeReferenceUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -61,10 +62,8 @@
         BooleanUtils.values());
   }
 
-  // TODO(b/271822426): Reenable test.
-  @Ignore("b/271822426")
   @Test
-  public void test() throws Exception {
+  public void testR8() throws Exception {
     LinkedHashSet<ExternalStartupItem> startupList = new LinkedHashSet<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
@@ -121,10 +120,21 @@
         ExternalStartupMethod.builder()
             .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("b")))
             .build(),
-        // TODO(b/271822426): Update after rewriting startup profile.
-        /*ExternalSyntheticStartupMethod.builder()
-        .setSyntheticContextReference(Reference.classFromClass(B.class))
-        .build(),*/
+        ExternalStartupClass.builder()
+            .setClassReference(getSyntheticLambdaClassReference(B.class))
+            .build(),
+        ExternalStartupMethod.builder()
+            .setMethodReference(
+                MethodReferenceUtils.instanceConstructor(getSyntheticLambdaClassReference(B.class)))
+            .build(),
+        ExternalStartupMethod.builder()
+            .setMethodReference(
+                Reference.method(
+                    getSyntheticLambdaClassReference(B.class),
+                    "run",
+                    Collections.emptyList(),
+                    TypeReferenceUtils.getVoidType()))
+            .build(),
         ExternalStartupMethod.builder()
             .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0")))
             .build(),
@@ -140,8 +150,8 @@
       builder.add(
           Reference.classFromClass(Main.class),
           Reference.classFromClass(A.class),
-          getSyntheticLambdaClassReference(B.class),
-          Reference.classFromClass(C.class));
+          Reference.classFromClass(C.class),
+          getSyntheticLambdaClassReference(B.class));
     }
     if (!enableMinimalStartupDex || virtualFile == 1) {
       builder.add(getSyntheticLambdaClassReference(Main.class));
@@ -186,13 +196,15 @@
 
     public static void main(String[] args) {
       A.a();
-      Runnable r = System.currentTimeMillis() > 0 ? B.b() : Main::error;
+      Runnable r = System.currentTimeMillis() > 0 ? B.b() : error();
       r.run();
       C.c();
     }
 
-    static void error() {
-      throw new RuntimeException();
+    static Runnable error() {
+      return () -> {
+        throw new RuntimeException();
+      };
     }
   }
 
@@ -203,6 +215,8 @@
     }
   }
 
+  // Class B will be pruned as a result of inlining, yet the synthetic derived from B.b() remains in
+  // the startup list.
   static class B {
 
     static Runnable b() {
diff --git a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
index c7c5eef..5cf70dd 100644
--- a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
@@ -9,6 +9,7 @@
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.equalTo;
 
+import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
@@ -23,7 +24,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Collection;
 import java.util.Collections;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -41,29 +41,35 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  // TODO(b/271822426): Reenable test.
-  @Ignore("b/271822426")
   @Test
   public void testD8() throws Exception {
+    // In D8 the startup profile is used to relayout an existing apk. Therefore, we first compile
+    // the program to dex, and then relayout the dex using D8 with a startup profile.
+    D8TestCompileResult compileResult =
+        testForD8(Backend.DEX)
+            .addProgramClasses(Main.class)
+            .release()
+            .setMinApi(AndroidApiLevel.LATEST)
+            .compile();
     testForD8(Backend.DEX)
-        .addProgramClasses(Main.class)
+        .addProgramFiles(compileResult.writeToZip())
         .addStartupProfileProviders(getStartupProfileProviders())
         .release()
         .setMinApi(AndroidApiLevel.LATEST)
         .compileWithExpectedDiagnostics(this::inspectDiagnostics);
   }
 
-  // TODO(b/271822426): Reenable test.
-  @Ignore("b/271822426")
   @Test
   public void testR8() throws Exception {
+    // In R8 we expect a startup profile that matches the input app. Since profiles gathered from
+    // running on ART will include synthetics, and these synthetics are not in the input app, we do
+    // not raise warnings if some rules in the profile do not match anything.
     testForR8(Backend.DEX)
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
         .addStartupProfileProviders(getStartupProfileProviders())
-        .allowDiagnosticWarningMessages()
         .setMinApi(AndroidApiLevel.LATEST)
-        .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
   }
 
   private static Collection<StartupProfileProvider> getStartupProfileProviders() {
@@ -73,7 +79,6 @@
           public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
             ClassReference fooClassReference = Reference.classFromTypeName("Foo");
             ClassReference barClassReference = Reference.classFromTypeName("Bar");
-            ClassReference bazClassReference = Reference.classFromTypeName("Baz");
             ClassReference jDollarClassReference = Reference.classFromTypeName("j$.Foo");
             startupProfileBuilder
                 .addStartupClass(
@@ -82,11 +87,6 @@
                     startupMethodBuilder ->
                         startupMethodBuilder.setMethodReference(
                             MethodReferenceUtils.mainMethod(barClassReference)))
-                // TODO(b/271822426): Update after rewriting startup profile.
-                /*.addSyntheticStartupMethod(
-                syntheticStartupMethodBuilder ->
-                    syntheticStartupMethodBuilder.setSyntheticContextReference(
-                        bazClassReference))*/
                 .addStartupClass(
                     startupClassBuilder ->
                         startupClassBuilder.setClassReference(jDollarClassReference));
@@ -108,7 +108,6 @@
                 equalTo(
                     StringUtils.joinLines(
                         "Startup method not found: void Bar.main(java.lang.String[])",
-                        "Startup class not found: Baz",
                         "Startup class not found: Foo")))));
   }