blob: d9eb62b5d08f18977a1b6357bcf0f806c0e566d1 [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.tracereferences;
import static com.android.tools.r8.utils.MissingDefinitionsDiagnosticTestUtils.getMissingClassMessage;
import static com.android.tools.r8.utils.MissingDefinitionsDiagnosticTestUtils.getMissingFieldMessage;
import static com.android.tools.r8.utils.MissingDefinitionsDiagnosticTestUtils.getMissingMethodMessage;
import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.diagnostic.DefinitionContext;
import com.android.tools.r8.diagnostic.internal.DefinitionMethodContextImpl;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
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;
@RunWith(Parameterized.class)
public class TraceReferencesDiagnosticTest extends TestBase {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
public TraceReferencesDiagnosticTest(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@Test
public void traceReferencesDiagnosticClassesFieldsAndMethods() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
ZipBuilder.builder(dir.resolve("target.jar"))
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class),
transformer(Target.class)
.removeFields(
(access, name, descriptor, signature, value) ->
name.equals("missingField1"))
.removeFields(
(access, name, descriptor, signature, value) ->
name.equals("missingField2"))
.removeMethods(
(access, name, descriptor, signature, exceptions) ->
name.equals("missingMethod"))
.transform())
.build();
Path sourceJar =
ZipBuilder.builder(dir.resolve("source.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Source.class))
.build();
try {
testForTraceReferences()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addSourceFiles(sourceJar)
.addTargetFiles(targetJar)
.setConsumer(new TraceReferencesCheckConsumer(TraceReferencesConsumer.emptyConsumer()))
.traceWithExpectedDiagnostics(
diagnostics -> {
DefinitionContext referencedFrom =
DefinitionMethodContextImpl.builder()
.setMethodContext(
Reference.methodFromMethod(Source.class.getDeclaredMethod("source")))
.setOrigin(getOrigin(Source.class))
.build();
diagnostics.inspectErrors(
diagnostic ->
diagnostic
.assertIsMissingDefinitionsDiagnostic()
.assertHasMessage(
StringUtils.joinLines(
getMissingClassMessage(Target1.class, referencedFrom),
getMissingMethodMessage(
Reference.methodFromMethod(
Target1.class.getDeclaredConstructor()),
referencedFrom),
getMissingClassMessage(Target2.class, referencedFrom),
getMissingMethodMessage(
Reference.methodFromMethod(
Target2.class.getDeclaredConstructor()),
referencedFrom),
getMissingClassMessage(Target3.class, referencedFrom),
getMissingMethodMessage(
Reference.methodFromMethod(
Target3.class.getDeclaredConstructor()),
referencedFrom),
getMissingFieldMessage(
Reference.fieldFromField(
Target.class.getDeclaredField("missingField1")),
referencedFrom),
getMissingFieldMessage(
Reference.fieldFromField(
Target.class.getDeclaredField("missingField2")),
referencedFrom),
getMissingMethodMessage(
Reference.methodFromMethod(
Target.class.getDeclaredMethod("missingMethod")),
referencedFrom)))
.assertIsAllMissingClasses(Target1.class, Target2.class, Target3.class)
.assertIsAllMissingFields(
Reference.fieldFromField(Target.class.getField("missingField1")),
Reference.fieldFromField(Target.class.getField("missingField2")))
.assertIsAllMissingMethods(
Reference.methodFromMethod(Target1.class.getDeclaredConstructor()),
Reference.methodFromMethod(Target2.class.getDeclaredConstructor()),
Reference.methodFromMethod(Target3.class.getDeclaredConstructor()),
Reference.methodFromMethod(
Target.class.getMethod("missingMethod"))));
});
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
}
@Test
public void traceReferencesDiagnosticFieldsAndMethods() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
ZipBuilder.builder(dir.resolve("target.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Target1.class),
ToolHelper.getClassFileForTestClass(Target2.class),
ToolHelper.getClassFileForTestClass(Target3.class))
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class),
transformer(Target.class)
.removeFields(
(access, name, descriptor, signature, value) ->
name.equals("missingField1"))
.removeFields(
(access, name, descriptor, signature, value) ->
name.equals("missingField2"))
.removeMethods(
(access, name, descriptor, signature, exceptions) ->
name.equals("missingMethod"))
.transform())
.build();
Path sourceJar =
ZipBuilder.builder(dir.resolve("source.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Source.class))
.build();
try {
testForTraceReferences()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addSourceFiles(sourceJar)
.addTargetFiles(targetJar)
.setConsumer(new TraceReferencesCheckConsumer(TraceReferencesConsumer.emptyConsumer()))
.traceWithExpectedDiagnostics(
diagnostics -> {
DefinitionContext referencedFrom =
DefinitionMethodContextImpl.builder()
.setMethodContext(
Reference.methodFromMethod(Source.class.getDeclaredMethod("source")))
.setOrigin(getOrigin(Source.class))
.build();
diagnostics.inspectErrors(
diagnostic ->
diagnostic
.assertIsMissingDefinitionsDiagnostic()
.assertHasMessage(
StringUtils.joinLines(
getMissingFieldMessage(
Reference.fieldFromField(
Target.class.getDeclaredField("missingField1")),
referencedFrom),
getMissingFieldMessage(
Reference.fieldFromField(
Target.class.getDeclaredField("missingField2")),
referencedFrom),
getMissingMethodMessage(
Reference.methodFromMethod(
Target.class.getDeclaredMethod("missingMethod")),
referencedFrom)))
.assertNoMissingClasses()
.assertIsAllMissingFields(
Reference.fieldFromField(Target.class.getField("missingField1")),
Reference.fieldFromField(Target.class.getField("missingField2")))
.assertIsAllMissingMethods(
Reference.methodFromMethod(
Target.class.getMethod("missingMethod"))));
});
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
}
@Test
public void traceReferencesDiagnosticMethods() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
ZipBuilder.builder(dir.resolve("target.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Target1.class),
ToolHelper.getClassFileForTestClass(Target2.class),
ToolHelper.getClassFileForTestClass(Target3.class))
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class),
transformer(Target.class)
.removeMethods(
(access, name, descriptor, signature, exceptions) ->
name.equals("missingMethod"))
.transform())
.build();
Path sourceJar =
ZipBuilder.builder(dir.resolve("source.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Source.class))
.build();
try {
testForTraceReferences()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addSourceFiles(sourceJar)
.addTargetFiles(targetJar)
.setConsumer(new TraceReferencesCheckConsumer(TraceReferencesConsumer.emptyConsumer()))
.traceWithExpectedDiagnostics(
diagnostics -> {
DefinitionContext referencedFrom =
DefinitionMethodContextImpl.builder()
.setMethodContext(
Reference.methodFromMethod(Source.class.getDeclaredMethod("source")))
.setOrigin(getOrigin(Source.class))
.build();
diagnostics.inspectErrors(
diagnostic ->
diagnostic
.assertIsMissingDefinitionsDiagnostic()
.assertHasMessage(
getMissingMethodMessage(
Reference.methodFromMethod(
Target.class.getDeclaredMethod("missingMethod")),
referencedFrom))
.assertNoMissingClasses()
.assertNoMissingFields()
.assertIsAllMissingMethods(
Reference.methodFromMethod(
Target.class.getMethod("missingMethod"))));
});
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
}
static class FailingConsumer implements TraceReferencesConsumer {
private final String where;
private boolean reported;
FailingConsumer(String where) {
this.where = where;
}
@Override
public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
if (!reported && where.equals("acceptType")) {
handler.error(new StringDiagnostic("Error in " + where));
reported = true;
}
}
@Override
public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
if (!reported && where.equals("acceptField")) {
handler.error(new StringDiagnostic("Error in " + where));
reported = true;
}
}
@Override
public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
if (!reported && where.equals("acceptMethod")) {
handler.error(new StringDiagnostic("Error in " + where));
reported = true;
}
}
@Override
public void finished(DiagnosticsHandler handler) {
if (!reported && where.equals("finished")) {
handler.error(new StringDiagnostic("Error in " + where));
reported = true;
}
}
}
@Test
public void traceReferencesConsumerError() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
ZipBuilder.builder(dir.resolve("target.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Target.class),
ToolHelper.getClassFileForTestClass(Target1.class),
ToolHelper.getClassFileForTestClass(Target2.class),
ToolHelper.getClassFileForTestClass(Target3.class))
.build();
Path sourceJar =
ZipBuilder.builder(dir.resolve("source.jar"))
.addFilesRelative(
ToolHelper.getClassPathForTests(),
ToolHelper.getClassFileForTestClass(Source.class))
.build();
for (String where : new String[] {"acceptType", "acceptField", "acceptMethod", "finished"}) {
TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
try {
TraceReferences.run(
TraceReferencesCommand.builder(testDiagnosticMessages)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addSourceFiles(sourceJar)
.addTargetFiles(targetJar)
.setConsumer(new FailingConsumer(where))
.build());
fail("Unexpected success");
} catch (CompilationFailedException e) {
// Expected.
}
testDiagnosticMessages.inspectErrors(
diagnostic ->
diagnostic.assertIsStringDiagnostic().assertHasMessage("Error in " + where));
}
}
static class Target1 {}
static class Target2 {}
static class Target3 {}
static class Target {
public static int missingField1;
public static int missingField2;
public static void missingMethod() {}
}
static class Source {
public static void source() {
new Target1();
new Target2();
new Target3();
Target.missingField1 = 1;
Target.missingField2 = 2;
Target.missingMethod();
}
}
}