|  | // 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.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 implements DiagnosticsHandler, TestDiagnosticMessages { | 
|  | 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) { | 
|  | // When testing D8 with class file output this warning is always emitted. Discard this, as | 
|  | // for tests this is not relevant. | 
|  | if (!warning.equals("Compiling to Java class files with D8 is not officially supported")) { | 
|  | 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, m -> m.getDiagnosticMessage())), | 
|  | 0, | 
|  | messages.size()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public TestDiagnosticMessages assertNoMessages() { | 
|  | assertEmpty("info", getInfos()); | 
|  | assertEmpty("warning", getWarnings()); | 
|  | assertEmpty("error", getErrors()); | 
|  | 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; | 
|  | } | 
|  | } |