blob: 713578f96afba2c422344aed59c602fed61cea31 [file] [log] [blame]
// Copyright (c) 2017, 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;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import org.hamcrest.Matcher;
public class TestDiagnosticMessagesImpl extends TestDiagnosticMessages
implements DiagnosticsHandler {
private final List<Diagnostic> infos = new ArrayList<>();
private final List<Diagnostic> warnings = new ArrayList<>();
private final List<Diagnostic> errors = new ArrayList<>();
BiFunction<DiagnosticsLevel, Diagnostic, DiagnosticsLevel> modifier;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Infos: ").append('\n');
for (Diagnostic info : infos) {
builder.append(" - ").append(info.getDiagnosticMessage()).append('\n');
}
builder.append("Warnings: ").append('\n');
for (Diagnostic warning : warnings) {
builder.append(" - ").append(warning.getDiagnosticMessage()).append('\n');
}
builder.append("Errors: ").append('\n');
for (Diagnostic error : errors) {
builder.append(" - ").append(error.getDiagnosticMessage()).append('\n');
}
return builder.toString();
}
@Override
public void info(Diagnostic info) {
// We are almost always compiling with assertions enabled and R8 will print a message. We
// discard the message here because for almost all tests, this message is not relevant.
if (!info.getDiagnosticMessage()
.equals("Running R8 version " + Version.LABEL + " with assertions enabled.")) {
infos.add(info);
}
}
@Override
public void warning(Diagnostic warning) {
warnings.add(warning);
}
@Override
public void error(Diagnostic error) {
errors.add(error);
}
@Override
public DiagnosticsLevel modifyDiagnosticsLevel(DiagnosticsLevel level, Diagnostic diagnostic) {
return modifier == null ? level : modifier.apply(level, diagnostic);
}
@Override
public List<Diagnostic> getInfos() {
return infos;
}
@Override
public List<Diagnostic> getWarnings() {
return warnings;
}
@Override
public List<Diagnostic> getErrors() {
return errors;
}
private void assertEmpty(String type, List<Diagnostic> messages) {
assertEquals(
"Expected no "
+ type
+ " messages, got:\n"
+ String.join("\n", ListUtils.map(messages, Diagnostic::getDiagnosticMessage)),
0,
messages.size());
}
@Override
public TestDiagnosticMessages assertNoMessages() {
assertEmpty("info", getInfos());
assertEmpty("warning", getWarnings());
assertEmpty("error", getErrors());
return this;
}
@Override
public TestDiagnosticMessages assertHasWarnings() {
assertFalse(warnings.isEmpty());
return this;
}
@Override
public TestDiagnosticMessages assertOnlyInfos() {
assertNotEquals(0, getInfos().size());
assertEmpty("warning", getWarnings());
assertEmpty("error", getErrors());
return this;
}
@Override
public TestDiagnosticMessages assertOnlyWarnings() {
assertEmpty("info", getInfos());
assertNotEquals(0, getWarnings().size());
assertEmpty("error", getErrors());
return this;
}
@Override
public TestDiagnosticMessages assertOnlyErrors() {
assertEmpty("info", getInfos());
assertEmpty("warning", getWarnings());
assertNotEquals(0, getErrors().size());
return this;
}
@Override
public TestDiagnosticMessages assertInfosCount(int count) {
assertEquals(count, getInfos().size());
return this;
}
@Override
public TestDiagnosticMessages assertWarningsCount(int count) {
assertEquals(count, getWarnings().size());
return this;
}
@Override
public TestDiagnosticMessages assertErrorsCount(int count) {
assertEquals(count, getErrors().size());
return this;
}
private TestDiagnosticMessages assertAllDiagnosticsMatches(
Iterable<Diagnostic> diagnostics, String tag, Matcher<Diagnostic> matcher) {
for (Diagnostic diagnostic : diagnostics) {
assertThat(diagnostic, matcher);
}
return this;
}
private TestDiagnosticMessages assertDiagnosticThatMatches(
Iterable<Diagnostic> diagnostics, String tag, Matcher<Diagnostic> matcher) {
int numberOfDiagnostics = 0;
for (Diagnostic diagnostic : diagnostics) {
if (matcher.matches(diagnostic)) {
return this;
}
numberOfDiagnostics++;
}
StringBuilder builder = new StringBuilder("No " + tag + " matches " + matcher.toString());
builder.append(System.lineSeparator());
if (numberOfDiagnostics == 0) {
builder.append("There were no " + tag + "s.");
} else {
builder.append("There were " + numberOfDiagnostics + " " + tag + "s:");
builder.append(System.lineSeparator());
for (Diagnostic diagnostic : diagnostics) {
builder.append(diagnostic.getDiagnosticMessage());
builder.append(System.lineSeparator());
}
}
fail(builder.toString());
return this;
}
private static void assertDiagnosticsMatch(
Iterable<Diagnostic> diagnostics, String tag, Collection<Matcher<Diagnostic>> matchers) {
// Match is unordered, but we make no attempts to find the maximum match.
int diagnosticsCount = 0;
Set<Diagnostic> matchedDiagnostics = new HashSet<>();
Set<Matcher<Diagnostic>> matchedMatchers = new HashSet<>();
for (Diagnostic diagnostic : diagnostics) {
diagnosticsCount++;
for (Matcher<Diagnostic> matcher : matchers) {
if (matchedMatchers.contains(matcher)) {
continue;
}
if (matcher.matches(diagnostic)) {
matchedDiagnostics.add(diagnostic);
matchedMatchers.add(matcher);
break;
}
}
}
StringBuilder builder = new StringBuilder();
boolean failedMatching = false;
if (matchedDiagnostics.size() < diagnosticsCount) {
failedMatching = true;
builder.append("\nUnmatched diagnostics:");
for (Diagnostic diagnostic : diagnostics) {
if (!matchedDiagnostics.contains(diagnostic)) {
builder
.append("\n - ")
.append(diagnostic.getClass().getName())
.append(": ")
.append(diagnostic.getDiagnosticMessage());
}
}
}
if (matchedMatchers.size() < matchers.size()) {
failedMatching = true;
builder.append("\nUnmatched matchers:");
for (Matcher<Diagnostic> matcher : matchers) {
if (!matchedMatchers.contains(matcher)) {
builder.append("\n - ").append(matcher);
}
}
}
if (failedMatching) {
builder.append("\nAll diagnostics:");
for (Diagnostic diagnostic : diagnostics) {
builder
.append("\n - ")
.append(diagnostic.getClass().getName())
.append(": ")
.append(diagnostic.getDiagnosticMessage());
}
builder.append("\nAll matchers:");
for (Matcher<Diagnostic> matcher : matchers) {
builder.append("\n - ").append(matcher);
}
fail(builder.toString());
}
// Double check consistency.
assertEquals(matchers.size(), diagnosticsCount);
assertEquals(diagnosticsCount, matchedDiagnostics.size());
assertEquals(diagnosticsCount, matchedMatchers.size());
}
@Override
public TestDiagnosticMessages assertDiagnosticsMatch(Collection<Matcher<Diagnostic>> matchers) {
assertDiagnosticsMatch(getAllDiagnostics(), "diagnostics", matchers);
return this;
}
@Override
public TestDiagnosticMessages assertInfosMatch(Collection<Matcher<Diagnostic>> matchers) {
assertDiagnosticsMatch(getInfos(), "infos", matchers);
return this;
}
@Override
public TestDiagnosticMessages assertWarningsMatch(Collection<Matcher<Diagnostic>> matchers) {
assertDiagnosticsMatch(getWarnings(), "warnings", matchers);
return this;
}
@Override
public TestDiagnosticMessages assertErrorsMatch(Collection<Matcher<Diagnostic>> matchers) {
assertDiagnosticsMatch(getErrors(), "errors", matchers);
return this;
}
@Override
public TestDiagnosticMessages assertDiagnosticThatMatches(Matcher<Diagnostic> matcher) {
return assertDiagnosticThatMatches(getAllDiagnostics(), "diagnostic message", matcher);
}
private Iterable<Diagnostic> getAllDiagnostics() {
return Iterables.concat(getInfos(), getWarnings(), getErrors());
}
@Override
public TestDiagnosticMessages assertInfoThatMatches(Matcher<Diagnostic> matcher) {
return assertDiagnosticThatMatches(getInfos(), "info", matcher);
}
@Override
public TestDiagnosticMessages assertWarningThatMatches(Matcher<Diagnostic> matcher) {
return assertDiagnosticThatMatches(getWarnings(), "warning", matcher);
}
@Override
public TestDiagnosticMessages assertErrorThatMatches(Matcher<Diagnostic> matcher) {
return assertDiagnosticThatMatches(getErrors(), "error", matcher);
}
@Override
public TestDiagnosticMessages assertAllDiagnosticsMatch(Matcher<Diagnostic> matcher) {
return assertAllDiagnosticsMatches(getAllDiagnostics(), "diagnostic message", matcher);
}
@Override
public TestDiagnosticMessages assertAllInfosMatch(Matcher<Diagnostic> matcher) {
return assertAllDiagnosticsMatches(getInfos(), "info", matcher);
}
@Override
public TestDiagnosticMessages assertAllWarningsMatch(Matcher<Diagnostic> matcher) {
return assertAllDiagnosticsMatches(getWarnings(), "warning", matcher);
}
@Override
public TestDiagnosticMessages assertAllErrorsMatch(Matcher<Diagnostic> matcher) {
return assertAllDiagnosticsMatches(getErrors(), "error", matcher);
}
void setDiagnosticsLevelModifier(
BiFunction<DiagnosticsLevel, Diagnostic, DiagnosticsLevel> modifier) {
this.modifier = modifier;
}
}