Test force enable assertions with JaCoCo instrumented code
Reproduction of b/169866678.
Bug: 169866678
Change-Id: I0a0177faf479b53054532456f7fb7b901cea2da8
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index a4cef91..bfd8334 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -339,6 +339,16 @@
}
/**
+ * Convert class to a binary name.
+ *
+ * @param clazz a java.lang.Class reference
+ * @return class binary name i.e. "java/lang/Object"
+ */
+ public static String getClassBinaryName(Class<?> clazz) {
+ return getBinaryNameFromJavaType(clazz.getTypeName());
+ }
+
+ /**
* Get package java name from a class descriptor.
*
* @param descriptor a class descriptor i.e. "Ljava/lang/Object;"
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d150f73..d37c554 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -161,6 +161,7 @@
private static final String PROGUARD = PROGUARD5_2_1;
public static final String JACOCO_AGENT =
"third_party/jacoco/0.8.2/org.jacoco.agent-0.8.2-runtime.jar";
+ public static final String JACOCO_CLI = "third_party/jacoco/0.8.6/lib/jacococli.jar";
public static final String PROGUARD_SETTINGS_FOR_INTERNAL_APPS = "third_party/proguardsettings/";
private static final String RETRACE6_0_1 = "third_party/proguard/proguard6.0.1/bin/retrace";
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
new file mode 100644
index 0000000..159ee81
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2020, 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.rewrite.assertions;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.testclasses.A;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class AssertionsConfigurationJacocoTest extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public AssertionsConfigurationJacocoTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClasses(TestClass.class, MockJacocoInit.class)
+ .addProgramClassFileData(transformClassWithJacocoInstrumentation(A.class))
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ // TODO(b/169866678): Should be "AssertionError in A".
+ .assertSuccessWithOutput(StringUtils.lines());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, MockJacocoInit.class)
+ .addProgramClassFileData(transformClassWithJacocoInstrumentation(A.class))
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(A.class, MockJacocoInit.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ // TODO(b/169866678): Should be "AssertionError in A".
+ .assertSuccessWithOutput(StringUtils.lines());
+ }
+
+ private byte[] transformClassWithJacocoInstrumentation(Class<?> clazz) throws IOException {
+ // Instrument the class with Jacoco.
+ Path dir = temp.newFolder().toPath();
+ runJacoco(ToolHelper.getClassFileForTestClass(clazz), dir);
+ // Rewrite the invocation of Jacoco initialization (getProbes invocation) for class with a Mock.
+ Path jacoco =
+ dir.resolve(
+ A.class.getTypeName().substring(clazz.getPackage().getName().length() + 1) + ".class");
+ return transformer(jacoco, Reference.classFromClass(clazz))
+ .transformMethodInsnInMethod(
+ "$jacocoInit",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ assertEquals(INVOKESTATIC, opcode);
+ assertEquals("getProbes", name);
+ continuation.visitMethodInsn(
+ INVOKESTATIC,
+ DescriptorUtils.getClassBinaryName(MockJacocoInit.class),
+ "getProbes",
+ descriptor,
+ isInterface);
+ })
+ .transform();
+ }
+
+ private void runJacoco(Path input, Path outdir) throws IOException {
+ List<String> cmdline = new ArrayList<>();
+ cmdline.add(TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString());
+ cmdline.add("-jar");
+ cmdline.add(ToolHelper.JACOCO_CLI);
+ cmdline.add("instrument");
+ cmdline.add(input.toString());
+ cmdline.add("--dest");
+ cmdline.add(outdir.toString());
+ ProcessBuilder builder = new ProcessBuilder(cmdline);
+ ProcessResult javacResult = ToolHelper.runProcess(builder);
+ assertEquals(javacResult.toString(), 0, javacResult.exitCode);
+ }
+
+ public static class MockJacocoInit {
+
+ public static boolean[] getProbes(long a, String b, int c) {
+ // For the test class A an array of size 6 is sufficient.
+ return new boolean[6];
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ try {
+ A.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in A");
+ }
+ }
+ }
+
+ // The test class A cannot be an inner class of AssertionsConfigurationJacocoTest, as then the
+ // desiredAssertionStatus() on the outer AssertionsConfigurationJacocoTest will be used and
+ // and that is not part of the input.
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java
new file mode 100644
index 0000000..2115568
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.rewrite.assertions.testclasses;
+
+public class A {
+
+ public static void m() {
+ assert false;
+ }
+}