Add additional tests for not synthesizing null check

Bug: 206427323
Bug: 215339687
Bug: 214377135
Change-Id: I55ebcc7b5272f682270e31e3d9ece9e6892fc43f
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
new file mode 100644
index 0000000..15466f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
@@ -0,0 +1,61 @@
+// 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.naming.retrace;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceInlineConditionTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Throwable {
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(Main.class)
+            .compile()
+            .inspectProguardMap(
+                map -> {
+                  // TODO(b/215339687): We should not have a rewriteFrame in the mapping file since
+                  //  an explicit null check should be inserted.
+                  assertThat(map, CoreMatchers.containsString("com.android.tools.r8.rewriteFrame"));
+                });
+  }
+
+  static class Foo {
+
+    void inlinable(boolean loop) {
+      while (loop) {}
+      String string = toString();
+      System.out.println(string);
+    }
+  }
+
+  static class Main {
+    public static void main(String[] args) {
+      Foo foo = (args.length == 0 ? null : new Foo());
+      foo.inlinable(args.length == 0);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
new file mode 100644
index 0000000..6143522
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -0,0 +1,116 @@
+// 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+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.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetraceInlineeWithNullCheckInlinedTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public boolean throwReceiverNpe;
+
+  @Parameters(name = "{0}, throwReceiverNpe: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+        BooleanUtils.values());
+  }
+
+  public StackTrace expectedStackTrace;
+
+  @Before
+  public void setup() throws Exception {
+    // Get the expected stack trace by running on the JVM.
+    expectedStackTrace =
+        testForRuntime(parameters)
+            .addProgramClasses(Caller.class, Foo.class)
+            .run(parameters.getRuntime(), Caller.class, getArgs())
+            .getStackTrace();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Caller.class)
+        .addKeepAttributeLineNumberTable()
+        .addKeepAttributeSourceFile()
+        .setMinApi(parameters.getApiLevel())
+        .enableInliningAnnotations()
+        .enableNoMethodStaticizingAnnotations()
+        .run(parameters.getRuntime(), Caller.class, getArgs())
+        .assertFailureWithErrorThatThrows(NullPointerException.class)
+        .inspectStackTrace(
+            (stackTrace, inspector) -> {
+              ClassSubject callerClass = inspector.clazz(Caller.class);
+              assertThat(callerClass, isPresent());
+              MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller");
+              assertThat(staticized, isPresentAndRenamed());
+              // TODO(b/214377135): The stack traces should be the same (when 206427323) is
+              // resolved.
+              assertThat(stackTrace, notIf(isSame(expectedStackTrace), throwReceiverNpe));
+            });
+  }
+
+  private String[] getArgs() {
+    return throwReceiverNpe ? new String[] {"Foo"} : new String[0];
+  }
+
+  static class Foo {
+    @NeverInline
+    @NoMethodStaticizing
+    Object notInlinable() {
+      System.out.println("Hello, world!");
+      throw new NullPointerException("Foo");
+    }
+
+    Object inlinable() {
+      return notInlinable();
+    }
+  }
+
+  static class Caller {
+
+    static void caller(Foo f) {
+      Object inlinable = f.inlinable();
+      System.out.println(inlinable);
+    }
+
+    @NeverInline
+    public static void outerCaller(Foo f) {
+      // caller should be inlined here as is. When inlinable is inlined into caller, it is done so
+      // without synthesizing a null check since the call notInlineable will throw an NPE if not
+      // staticized. We have this outer caller to check that stack traces still work correctly
+      // when the bit on throwing NPE is inlined.
+      caller(f);
+    }
+
+    public static void main(String[] args) {
+      outerCaller(args.length == 0 ? new Foo() : null);
+    }
+  }
+}