blob: c230c00a100ff6b6e070f8341c917cf8ca2e52c9 [file] [log] [blame]
// Copyright (c) 2018, 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.whyareyoukeeping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
import com.android.tools.r8.utils.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
class A {
public void foo() {
bar();
}
@NeverInline
public void bar() {
baz();
}
@NeverInline
public void baz() {
System.out.println("called baz");
}
}
@RunWith(Parameterized.class)
public class WhyAreYouKeepingTest extends TestBase {
public static final String expected =
StringUtils.lines(
"com.android.tools.r8.shaking.whyareyoukeeping.A",
"|- is referenced in keep rule:",
"| -keep class com.android.tools.r8.shaking.whyareyoukeeping.A { void foo(); }");
public static final String expectedPathToBaz =
StringUtils.lines(
"void com.android.tools.r8.shaking.whyareyoukeeping.A.baz()",
"|- is invoked from:",
"| void com.android.tools.r8.shaking.whyareyoukeeping.A.bar()",
"|- is invoked from:",
"| void com.android.tools.r8.shaking.whyareyoukeeping.A.foo()",
"|- is referenced in keep rule:",
"| -keep class com.android.tools.r8.shaking.whyareyoukeeping.A { void foo(); }");
@Parameters(name = "{0}")
public static Backend[] parameters() {
return ToolHelper.getBackends();
}
public final Backend backend;
public WhyAreYouKeepingTest(Backend backend) {
this.backend = backend;
}
@Test
public void testWhyAreYouKeepingViaProguardConfig() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(backend)
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class " + A.class.getTypeName())
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.redirectStdOut(new PrintStream(baos))
.compile();
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals(expected, output);
}
@Test
public void testWhyAreYouKeepingViaConsumer() throws Exception {
WhyAreYouKeepingConsumer graphConsumer = new WhyAreYouKeepingConsumer(null);
testForR8(backend)
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.setKeptGraphConsumer(graphConsumer)
.compile();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphConsumer.printWhyAreYouKeeping(Reference.classFromClass(A.class), new PrintStream(baos));
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals(expected, output);
}
@Test
public void testWhyAreYouKeepingPathViaProguardConfig()
throws NoSuchMethodException, CompilationFailedException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(backend)
.addProgramClasses(A.class)
.enableInliningAnnotations()
.addKeepRules("-whyareyoukeeping class " + A.class.getTypeName() + " { baz(); }")
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.redirectStdOut(new PrintStream(baos))
.compile();
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals(expected + expectedPathToBaz, output);
}
@Test
public void testWhyAreYouKeepingPathViaConsumer()
throws NoSuchMethodException, CompilationFailedException {
WhyAreYouKeepingConsumer graphConsumer = new WhyAreYouKeepingConsumer(null);
testForR8(backend)
.addProgramClasses(A.class)
.setKeptGraphConsumer(graphConsumer)
.enableInliningAnnotations()
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.compile();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphConsumer.printWhyAreYouKeeping(
Reference.methodFromMethod(A.class.getMethod("baz")), new PrintStream(baos));
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals(expectedPathToBaz, output);
}
@Test
public void testNonExistentClassWhyAreYouKeepingViaProguardConfig() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(backend)
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class NonExistentClass")
.allowUnusedProguardConfigurationRules()
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.redirectStdOut(new PrintStream(baos))
.compile();
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
// Expected outcome is empty.
assertEquals("", output);
}
@Test
public void testNonExistentMethodWhyAreYouKeepingViaProguardConfig() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String aName = A.class.getTypeName();
testForR8(backend)
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.addKeepRules("-whyareyoukeeping class " + aName + " { nonExistentMethod(); }")
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.redirectStdOut(new PrintStream(baos))
.compile();
String output = new String(baos.toByteArray(), StandardCharsets.UTF_8);
// Expected outcome is empty.
assertFalse("b/122820741", output.isEmpty());
}
}