Start to split up backported method test by type

These test both correctness and desugaring on proper API levels.

Test: tools/test.py --dex_vm all --no-internal *desugar.backports.*
Test: tools/test.py --dex_vm all --no-internal *BackportedMethodRewriterTest*
Change-Id: I95659081ea63ff9867882b0b9cd8551a9af8d846
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 9890298..599cacb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.RewritableMethods.MethodGenerator;
+import com.android.tools.r8.ir.desugar.backports.BooleanMethods;
 import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -317,32 +318,6 @@
     }
   }
 
-  private static final class BooleanMethods extends TemplateMethodCode {
-    BooleanMethods(InternalOptions options, DexMethod method, String methodName) {
-      super(options, method, methodName, method.proto.toDescriptorString());
-    }
-
-    public static int hashCode(boolean b) {
-      return Boolean.valueOf(b).hashCode();
-    }
-
-    public static int compare(boolean a, boolean b) {
-      return Boolean.valueOf(a).compareTo(Boolean.valueOf(b));
-    }
-
-    public static boolean logicalAnd(boolean a, boolean b) {
-      return a && b;
-    }
-
-    public static boolean logicalOr(boolean a, boolean b) {
-      return a || b;
-    }
-
-    public static boolean logicalXor(boolean a, boolean b) {
-      return a ^ b;
-    }
-  }
-
   private static final class LongMethods extends TemplateMethodCode {
     LongMethods(InternalOptions options, DexMethod method, String methodName) {
       super(options, method, methodName, method.proto.toDescriptorString());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethods.java
new file mode 100644
index 0000000..514d464
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethods.java
@@ -0,0 +1,35 @@
+// 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.ir.desugar.backports;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
+import com.android.tools.r8.utils.InternalOptions;
+
+public final class BooleanMethods extends TemplateMethodCode {
+  public BooleanMethods(InternalOptions options, DexMethod method, String methodName) {
+    super(options, method, methodName, method.proto.toDescriptorString());
+  }
+
+  public static int hashCode(boolean b) {
+    return Boolean.valueOf(b).hashCode();
+  }
+
+  public static int compare(boolean a, boolean b) {
+    return Boolean.valueOf(a).compareTo(Boolean.valueOf(b));
+  }
+
+  public static boolean logicalAnd(boolean a, boolean b) {
+    return a && b;
+  }
+
+  public static boolean logicalOr(boolean a, boolean b) {
+    return a || b;
+  }
+
+  public static boolean logicalXor(boolean a, boolean b) {
+    return a ^ b;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java b/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
index 5eb581d..f840ec9 100644
--- a/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
@@ -11,6 +11,7 @@
 
 import com.android.tools.r8.D8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.desugar.backports.BooleanBackportTest;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -23,6 +24,10 @@
 import org.junit.Before;
 import org.junit.Test;
 
+/**
+ * @deprecated New tests should follow the pattern of {@link BooleanBackportTest}.
+ */
+@Deprecated
 public class BackportedMethodRewriterTest extends TestBase {
   static String expectedOutput = "";
 
@@ -40,9 +45,9 @@
         .run(TestMethods.class)
         .assertSuccessWithOutput(expectedOutput);
 
-    assertDesugaring(AndroidApiLevel.O, 70);
-    assertDesugaring(AndroidApiLevel.N, 49);
-    assertDesugaring(AndroidApiLevel.K, 20);
+    assertDesugaring(AndroidApiLevel.O, 65);
+    assertDesugaring(AndroidApiLevel.N, 44);
+    assertDesugaring(AndroidApiLevel.K, 19);
     assertDesugaring(AndroidApiLevel.J_MR2, 0);
   }
 
@@ -215,17 +220,6 @@
         }
       }
 
-      boolean[] aBooleans = { true, false };
-      for (boolean aBoolean : aBooleans) {
-        System.out.println(Boolean.hashCode(aBoolean));
-        for (boolean bBoolean : aBooleans) {
-          System.out.println(Boolean.compare(aBoolean, bBoolean));
-          System.out.println(Boolean.logicalAnd(aBoolean, bBoolean));
-          System.out.println(Boolean.logicalOr(aBoolean, bBoolean));
-          System.out.println(Boolean.logicalXor(aBoolean, bBoolean));
-        }
-      }
-
       long[] aLongs = new long[]{42L, 1L, -1L, Integer.MIN_VALUE, Integer.MAX_VALUE,
           Long.MAX_VALUE, Long.MIN_VALUE};
       long[] bLongs = new long[]{43L, 2L, -2L, Integer.MIN_VALUE, Integer.MAX_VALUE,
@@ -279,7 +273,9 @@
       System.out.println(Objects.compare("b", "a", reverse()));
       System.out.println(Objects.compare("a", "a", reverse()));
 
-      Object[] objects = { aBytes, aBooleans, aChars, aDoubles, aFloats, aInts, aLongs, "a", null };
+      Object[] objects = {
+          aBytes, new boolean[] { true, false }, aChars, aDoubles, aFloats, aInts, aLongs, "a", null
+      };
       for (Object aObject : objects) {
         for (Object bObject : objects) {
           System.out.println(Objects.deepEquals(aObject, bObject));
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
new file mode 100644
index 0000000..d139b48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -0,0 +1,104 @@
+// 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.desugar.backports;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static java.util.stream.Collectors.toList;
+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.codeinspector.ClassSubject;
+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.util.List;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+import org.junit.Assert;
+import org.junit.Test;
+
+abstract class AbstractBackportTest extends TestBase {
+  private final TestParameters parameters;
+  private final Class<?> targetClass;
+  private final Class<?> testClass;
+  private final NavigableMap<AndroidApiLevel, Integer> invokeStaticCounts = new TreeMap<>();
+
+  AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
+      Class<?> testClass) {
+    this.parameters = parameters;
+    this.targetClass = targetClass;
+    this.testClass = testClass;
+
+    // Assume all method calls will be rewritten on the lowest API level.
+    invokeStaticCounts.put(AndroidApiLevel.B, 0);
+  }
+
+  void registerTarget(AndroidApiLevel apiLevel, int invokeStaticCount) {
+    invokeStaticCounts.put(apiLevel, invokeStaticCount);
+  }
+
+  @Test
+  public void desugaring() throws Exception {
+    testForD8()
+        .addProgramClasses(testClass, MiniAssert.class)
+        .setMinApi(parameters.getRuntime().asDex().getMinApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), testClass)
+        .assertSuccess()
+        .inspect(this::assertDesugaring);
+  }
+
+  private void assertDesugaring(CodeInspector inspector) {
+    ClassSubject testSubject = inspector.clazz(testClass);
+    assertThat(testSubject, isPresent());
+
+    MethodSubject mainMethod = testSubject.mainMethod();
+    assertThat(mainMethod, isPresent());
+
+    List<InstructionSubject> javaInvokeStatics = mainMethod
+        .streamInstructions()
+        .filter(InstructionSubject::isInvoke)
+        .filter(is -> is.getMethod().holder.toSourceString().equals(targetClass.getName()))
+        .collect(toList());
+
+    AndroidApiLevel apiLevel = parameters.getRuntime().asDex().getMinApiLevel();
+    long expectedTargetInvokes = invokeStaticCounts.ceilingEntry(apiLevel).getValue();
+    long actualTargetInvokes = javaInvokeStatics.size();
+    assertEquals("Expected "
+        + expectedTargetInvokes
+        + " invokes on "
+        + targetClass.getName()
+        + " but found "
+        + actualTargetInvokes
+        + ": "
+        + javaInvokeStatics, expectedTargetInvokes, actualTargetInvokes);
+  }
+
+  /** JUnit {@link Assert} isn't available in the VM runtime. This is a mini mirror of its API. */
+  static abstract class MiniAssert {
+    static void assertTrue(boolean value) {
+      assertEquals(true, value);
+    }
+
+    static void assertFalse(boolean value) {
+      assertEquals(false, value);
+    }
+
+    static void assertEquals(boolean expected, boolean actual) {
+      if (expected != actual) {
+        throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+      }
+    }
+
+    static void assertEquals(int expected, int actual) {
+      if (expected != actual) {
+        throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
new file mode 100644
index 0000000..874b584
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
@@ -0,0 +1,52 @@
+// 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class BooleanBackportTest extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters().withDexRuntimes().build();
+  }
+
+  public BooleanBackportTest(TestParameters parameters) {
+    super(parameters, Boolean.class, Main.class);
+    registerTarget(AndroidApiLevel.N, 18);
+    registerTarget(AndroidApiLevel.K, 4);
+  }
+
+  static final class Main extends MiniAssert {
+    public static void main(String[] args) {
+      assertEquals(1231, Boolean.hashCode(true));
+      assertEquals(1237, Boolean.hashCode(false));
+
+      assertEquals(1, Boolean.compare(true, false));
+      assertEquals(0, Boolean.compare(true, true));
+      assertEquals(0, Boolean.compare(false, false));
+      assertEquals(-1, Boolean.compare(false, true));
+
+      assertTrue(Boolean.logicalAnd(true, true));
+      assertFalse(Boolean.logicalAnd(true, false));
+      assertFalse(Boolean.logicalAnd(false, true));
+      assertFalse(Boolean.logicalAnd(false, false));
+
+      assertTrue(Boolean.logicalOr(true, true));
+      assertTrue(Boolean.logicalOr(true, false));
+      assertTrue(Boolean.logicalOr(false, true));
+      assertFalse(Boolean.logicalOr(false, false));
+
+      assertFalse(Boolean.logicalXor(true, true));
+      assertTrue(Boolean.logicalXor(true, false));
+      assertTrue(Boolean.logicalXor(false, true));
+      assertFalse(Boolean.logicalXor(false, false));
+    }
+  }
+}