blob: 7cb0d57a1d085289495289422190923123b51da7 [file] [log] [blame]
// 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.shaking.addconfigurationdebugging;
import static com.android.tools.r8.graph.DexEncodedMethod.CONFIGURATION_DEBUGGING_PREFIX;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
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.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
class BaseClass {
Object field;
BaseClass(Object usedArg) {
field = usedArg;
}
}
class UninstantiatedClass extends BaseClass {
UninstantiatedClass() {
super(null);
System.out.println("UninstantiatedClass#<init>");
}
UninstantiatedClass(String arg) {
super(arg);
System.out.println("UninstantiatedClass#<init>(String)");
}
}
class TestClass {
BaseClass b;
TestClass() {
b = new BaseClass(this);
System.out.println(b);
}
void foo(int i, long l) {
System.out.println("void TestClass#foo(IJ)");
}
static void bar(TestClass arg) {
System.out.println("void TestClass#bar(TestClass)");
}
}
class Caller {
public static void main(String[] args) {
try {
new UninstantiatedClass();
} catch (RuntimeException e) {
}
try {
new UninstantiatedClass("aaarrrrrrhhhhhh");
} catch (RuntimeException e) {
}
TestClass instance = new TestClass();
try {
instance.foo(4, 2L);
} catch (RuntimeException e) {
}
try {
TestClass.bar(instance);
} catch (RuntimeException e) {
}
throw new RuntimeException("Reaching the end");
}
}
@RunWith(Parameterized.class)
public class ConfigurationDebuggingTest extends TestBase {
private static final String PACKAGE_NAME =
ConfigurationDebuggingTest.class.getPackage().getName();
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().build();
}
public ConfigurationDebuggingTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void test() throws Exception {
Path firstRunArchive =
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class, UninstantiatedClass.class, TestClass.class)
.addKeepRules("-addconfigurationdebugging")
.addKeepRules("-keep class **.TestClass { <init>(); }")
.noMinification()
.setMinApi(parameters.getRuntime())
.compile()
.inspect(this::inspect)
.writeToZip();
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
.addLibraryClasses(BaseClass.class, UninstantiatedClass.class, TestClass.class)
.addLibraryFiles(ToolHelper.getDefaultAndroidJar())
.addProgramClasses(Caller.class)
.addKeepMainRule(Caller.class)
.setMinApi(parameters.getRuntime());
R8TestRunResult result =
builder
.compile()
.addRunClasspathFiles(firstRunArchive)
.run(parameters.getRuntime(), Caller.class);
// TODO(b/117302947): Dex runtime should be able to find that framework class.
if (parameters.isDexRuntime()) {
result.assertFailureWithErrorThatMatches(containsString("NoClassDefFoundError"));
result.assertFailureWithErrorThatMatches(containsString("android.util.Log"));
return;
}
result
.assertFailureWithErrorThatMatches(
containsString(createExpectedMessage(UninstantiatedClass.class)))
.assertFailureWithErrorThatMatches(containsString("void <init>()"))
.assertFailureWithErrorThatMatches(containsString("void <init>(java.lang.String)"))
.assertFailureWithErrorThatMatches(
containsString(createExpectedMessage(TestClass.class)))
.assertFailureWithErrorThatMatches(containsString("void foo(int,long)"))
.assertFailureWithErrorThatMatches(
containsString("void bar(" + PACKAGE_NAME + ".TestClass" +")"))
.assertFailureWithErrorThatMatches(containsString("Reaching the end"));
}
private String createExpectedMessage(Class<?> clazz) {
return CONFIGURATION_DEBUGGING_PREFIX + clazz.getName();
}
private void inspect(CodeInspector inspector) {
ClassSubject baseClass = inspector.clazz(BaseClass.class);
assertThat(baseClass, isPresent());
baseClass.allMethods().forEach(methodSubject -> {
assertTrue(methodSubject.isInstanceInitializer());
assertFalse(hasThrow(methodSubject));
});
ClassSubject uninstantiatedClass = inspector.clazz(UninstantiatedClass.class);
assertThat(uninstantiatedClass, isPresent());
uninstantiatedClass.allMethods().forEach(methodSubject -> {
assertTrue(methodSubject.isInstanceInitializer());
assertTrue(hasThrow(methodSubject));
});
ClassSubject testClass = inspector.clazz(TestClass.class);
assertThat(testClass, isPresent());
MethodSubject foo = testClass.uniqueMethodWithName("foo");
assertThat(foo, isPresent());
assertTrue(hasThrow(foo));
MethodSubject bar = testClass.uniqueMethodWithName("bar");
assertThat(bar, isPresent());
assertTrue(hasThrow(bar));
}
private static boolean hasThrow(MethodSubject methodSubject) {
return methodSubject.iterateInstructions(InstructionSubject::isThrow).hasNext();
}
}