blob: 84bc19c91309f365d707f8a8d321166f90fbbbc0 [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.retrace;
import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousMissingLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithMultipleLineMappingsStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithSignatureNonVerboseStackTrace;
import com.android.tools.r8.retrace.stacktraces.AutoStackTrace;
import com.android.tools.r8.retrace.stacktraces.CircularReferenceStackTrace;
import com.android.tools.r8.retrace.stacktraces.ColonInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameWithInnerClassesStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
import com.android.tools.r8.retrace.stacktraces.MemberFieldOverlapStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleDotsInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
import com.android.tools.r8.retrace.stacktraces.NoObfuscationRangeMappingWithStackTrace;
import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfuscatedRangeToSingleLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
import com.android.tools.r8.retrace.stacktraces.SourceFileNameSynthesizeStackTrace;
import com.android.tools.r8.retrace.stacktraces.SourceFileWithNumberAndEmptyStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Ignore;
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 RetraceTests extends TestBase {
@Parameters(name = "{0}, use regular expression: {1}, external: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(), BooleanUtils.values(), BooleanUtils.values());
}
private final TestParameters testParameters;
private final boolean useRegExpParsing;
private final boolean external;
public RetraceTests(TestParameters parameters, boolean useRegExpParsing, boolean external) {
this.testParameters = parameters;
this.useRegExpParsing = useRegExpParsing;
this.external = external;
}
@Test
public void testCanMapExceptionClass() throws Exception {
runRetraceTest(new ObfucatedExceptionClassStackTrace());
}
@Test
public void testSuppressedStackTrace() throws Exception {
runRetraceTest(new SuppressedStackTrace());
}
@Test
public void testFileNameStackTrace() throws Exception {
runRetraceTest(new FileNameExtensionStackTrace());
}
@Test
public void testInlineFileNameStackTrace() throws Exception {
runRetraceTest(new InlineFileNameStackTrace());
}
@Test
public void testInlineFileNameWithInnerClassesStackTrace() throws Exception {
runRetraceTest(new InlineFileNameWithInnerClassesStackTrace());
}
@Test
public void testNoObfuscationRangeMappingWithStackTrace() throws Exception {
runRetraceTest(new NoObfuscationRangeMappingWithStackTrace());
}
@Test
public void testNullLineTrace() {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
NullStackTrace nullStackTrace = new NullStackTrace();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
.setProguardMapProducer(nullStackTrace::mapping)
.setStackTrace(nullStackTrace.obfuscatedStackTrace())
.setRetracedStackTraceConsumer(retraced -> fail())
.build();
try {
Retrace.run(retraceCommand);
fail();
} catch (RetraceAbortException e) {
diagnosticsHandler.assertOnlyErrors();
diagnosticsHandler.assertErrorsCount(1);
assertThat(
diagnosticsHandler.getErrors().get(0).getDiagnosticMessage(),
containsString("The stack trace line is <null>"));
}
}
@Test
public void testInvalidStackTraceLineWarnings() throws Exception {
InvalidStackTrace invalidStackTraceTest = new InvalidStackTrace();
runRetraceTest(invalidStackTraceTest).assertNoMessages();
}
@Test
public void testAssertionErrorInRetrace() throws Exception {
runRetraceTest(new RetraceAssertionErrorStackTrace());
}
@Test
public void testActualStackTraces() throws Exception {
List<ActualBotStackTraceBase> stackTraces =
ImmutableList.of(new ActualIdentityStackTrace(), new ActualRetraceBotStackTrace());
for (ActualBotStackTraceBase stackTrace : stackTraces) {
runRetraceTest(stackTrace)
.assertWarningsCount(useRegExpParsing ? 0 : stackTrace.expectedWarnings());
}
}
@Test
public void testAmbiguousStackTrace() throws Exception {
runRetraceTest(new AmbiguousStackTrace());
}
@Test
public void testAmbiguousMissingLineStackTrace() throws Exception {
runRetraceTest(new AmbiguousMissingLineStackTrace());
}
@Test
public void testAmbiguousMissingLineNotVerbose() throws Exception {
runRetraceTest(new AmbiguousWithSignatureNonVerboseStackTrace());
}
@Test
public void testAmbiguousMultipleMappingsTest() throws Exception {
runRetraceTest(new AmbiguousWithMultipleLineMappingsStackTrace());
}
@Test
public void testInliningWithLineNumbers() throws Exception {
runRetraceTest(new InlineWithLineNumbersStackTrace());
}
@Test
public void testInliningNoLineNumberInfoStackTraces() throws Exception {
runRetraceTest(new InlineNoLineNumberStackTrace());
}
@Test
public void testCircularReferenceStackTrace() throws Exception {
// Proguard retrace (and therefore the default regular expression) will not retrace circular
// reference exceptions.
assumeFalse(useRegExpParsing);
runRetraceTest(new CircularReferenceStackTrace());
}
@Test
public void testObfuscatedRangeToSingleLine() throws Exception {
runRetraceTest(new ObfuscatedRangeToSingleLineStackTrace());
}
@Test
@Ignore("b/170293908")
public void testBootLoaderAndNamedModulesStackTrace() throws Exception {
assumeFalse(useRegExpParsing);
runRetraceTest(new NamedModuleStackTrace());
}
@Test
public void testUnknownSourceStackTrace() throws Exception {
runRetraceTest(new UnknownSourceStackTrace());
}
@Test
public void testInlineSourceFileContext() throws Exception {
runRetraceTest(new InlineSourceFileContextStackTrace());
}
@Test
public void testColonInSourceFileNameStackTrace() throws Exception {
runRetraceTest(new ColonInFileNameStackTrace());
}
@Test
public void testMultipleDotsInFileNameStackTrace() throws Exception {
runRetraceTest(new MultipleDotsInFileNameStackTrace());
}
@Test
public void testUnicodeInFileNameStackTrace() throws Exception {
runRetraceTest(new UnicodeInFileNameStackTrace());
}
@Test
public void testMemberFieldOverlapStackTrace() throws Exception {
MemberFieldOverlapStackTrace stackTraceForTest = new MemberFieldOverlapStackTrace();
runRetraceTest(stackTraceForTest);
inspectRetraceTest(stackTraceForTest, stackTraceForTest::inspectField);
}
@Test
public void testSourceFileWithNumberAndEmptyStackTrace() throws Exception {
runRetraceTest(new SourceFileWithNumberAndEmptyStackTrace());
}
@Test
public void testSourceFileNameSynthesizeStackTrace() throws Exception {
runRetraceTest(new SourceFileNameSynthesizeStackTrace());
}
@Test
public void testAutoStackTrace() throws Exception {
runRetraceTest(new AutoStackTrace());
}
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
Retracer.createDefault(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
}
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest)
throws Exception {
if (external) {
assumeTrue(useRegExpParsing);
assumeTrue(testParameters.isCfRuntime());
// The external dependency is built on top of R8Lib. If test.py is run with
// no r8lib, do not try and run the external R8 Retrace since it has not been built.
assumeTrue(Files.exists(ToolHelper.R8LIB_JAR));
Path path = temp.newFolder().toPath();
Path mappingFile = path.resolve("mapping");
Files.write(mappingFile, stackTraceForTest.mapping().getBytes());
Path stackTraceFile = path.resolve("stacktrace.txt");
Files.write(
stackTraceFile,
StringUtils.joinLines(stackTraceForTest.obfuscatedStackTrace())
.getBytes(StandardCharsets.UTF_8));
List<String> command = new ArrayList<>();
command.add(testParameters.getRuntime().asCf().getJavaExecutable().toString());
command.add("-ea");
command.add("-cp");
command.add(ToolHelper.R8_RETRACE_JAR.toString());
command.add("com.android.tools.r8.retrace.Retrace");
command.add(mappingFile.toString());
command.add(stackTraceFile.toString());
command.add("-quiet");
ProcessBuilder builder = new ProcessBuilder(command);
ProcessResult processResult = ToolHelper.runProcess(builder);
assertEquals(
StringUtils.joinLines(stackTraceForTest.retracedStackTrace())
+ StringUtils.LINE_SEPARATOR,
processResult.stdout);
// TODO(b/177204438): Parse diagnostics from stdErr
return new TestDiagnosticMessagesImpl();
} else {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
.setProguardMapProducer(stackTraceForTest::mapping)
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
.setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
.setRetracedStackTraceConsumer(
retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
.build();
Retrace.run(retraceCommand);
return diagnosticsHandler;
}
}
}