blob: 11829f67c89783b2ea683cf6e7286aab729e4731 [file] [log] [blame]
// 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.codeinspector.CodeInspector;
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;
}
private void checkAssertionCodeEnabled(CodeInspector inspector) {
AssertionsCheckerUtils.checkAssertionCodeEnabled(inspector.clazz(A.class), "m");
}
@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)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkAssertionCodeEnabled)
.assertSuccessWithOutputLines("AssertionError in A");
}
@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)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkAssertionCodeEnabled)
.assertSuccessWithOutputLines("AssertionError in A");
}
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.
}