| package org.junit; |
| |
| /** |
| * Thrown when an {@link org.junit.Assert#assertEquals(Object, Object) assertEquals(String, String)} fails. |
| * Create and throw a <code>ComparisonFailure</code> manually if you want to show users the |
| * difference between two complex strings. |
| * <p/> |
| * Inspired by a patch from Alex Chaffee (alex@purpletech.com) |
| * |
| * @since 4.0 |
| */ |
| public class ComparisonFailure extends AssertionError { |
| /** |
| * The maximum length for expected and actual strings. If it is exceeded, the strings should be shortened. |
| * |
| * @see ComparisonCompactor |
| */ |
| private static final int MAX_CONTEXT_LENGTH = 20; |
| private static final long serialVersionUID = 1L; |
| |
| /* |
| * We have to use the f prefix until the next major release to ensure |
| * serialization compatibility. |
| * See https://github.com/junit-team/junit/issues/976 |
| */ |
| private String fExpected; |
| private String fActual; |
| |
| /** |
| * Constructs a comparison failure. |
| * |
| * @param message the identifying message or null |
| * @param expected the expected string value |
| * @param actual the actual string value |
| */ |
| public ComparisonFailure(String message, String expected, String actual) { |
| super(message); |
| this.fExpected = expected; |
| this.fActual = actual; |
| } |
| |
| /** |
| * Returns "..." in place of common prefix and "..." in place of common suffix between expected and actual. |
| * |
| * @see Throwable#getMessage() |
| */ |
| @Override |
| public String getMessage() { |
| return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage()); |
| } |
| |
| /** |
| * Returns the actual string value |
| * |
| * @return the actual string value |
| */ |
| public String getActual() { |
| return fActual; |
| } |
| |
| /** |
| * Returns the expected string value |
| * |
| * @return the expected string value |
| */ |
| public String getExpected() { |
| return fExpected; |
| } |
| |
| private static class ComparisonCompactor { |
| private static final String ELLIPSIS = "..."; |
| private static final String DIFF_END = "]"; |
| private static final String DIFF_START = "["; |
| |
| /** |
| * The maximum length for <code>expected</code> and <code>actual</code> strings to show. When |
| * <code>contextLength</code> is exceeded, the Strings are shortened. |
| */ |
| private final int contextLength; |
| private final String expected; |
| private final String actual; |
| |
| /** |
| * @param contextLength the maximum length of context surrounding the difference between the compared strings. |
| * When context length is exceeded, the prefixes and suffixes are compacted. |
| * @param expected the expected string value |
| * @param actual the actual string value |
| */ |
| public ComparisonCompactor(int contextLength, String expected, String actual) { |
| this.contextLength = contextLength; |
| this.expected = expected; |
| this.actual = actual; |
| } |
| |
| public String compact(String message) { |
| if (expected == null || actual == null || expected.equals(actual)) { |
| return Assert.format(message, expected, actual); |
| } else { |
| DiffExtractor extractor = new DiffExtractor(); |
| String compactedPrefix = extractor.compactPrefix(); |
| String compactedSuffix = extractor.compactSuffix(); |
| return Assert.format(message, |
| compactedPrefix + extractor.expectedDiff() + compactedSuffix, |
| compactedPrefix + extractor.actualDiff() + compactedSuffix); |
| } |
| } |
| |
| private String sharedPrefix() { |
| int end = Math.min(expected.length(), actual.length()); |
| for (int i = 0; i < end; i++) { |
| if (expected.charAt(i) != actual.charAt(i)) { |
| return expected.substring(0, i); |
| } |
| } |
| return expected.substring(0, end); |
| } |
| |
| private String sharedSuffix(String prefix) { |
| int suffixLength = 0; |
| int maxSuffixLength = Math.min(expected.length() - prefix.length(), |
| actual.length() - prefix.length()) - 1; |
| for (; suffixLength <= maxSuffixLength; suffixLength++) { |
| if (expected.charAt(expected.length() - 1 - suffixLength) |
| != actual.charAt(actual.length() - 1 - suffixLength)) { |
| break; |
| } |
| } |
| return expected.substring(expected.length() - suffixLength); |
| } |
| |
| private class DiffExtractor { |
| private final String sharedPrefix; |
| private final String sharedSuffix; |
| |
| /** |
| * Can not be instantiated outside {@link org.junit.ComparisonFailure.ComparisonCompactor}. |
| */ |
| private DiffExtractor() { |
| sharedPrefix = sharedPrefix(); |
| sharedSuffix = sharedSuffix(sharedPrefix); |
| } |
| |
| public String expectedDiff() { |
| return extractDiff(expected); |
| } |
| |
| public String actualDiff() { |
| return extractDiff(actual); |
| } |
| |
| public String compactPrefix() { |
| if (sharedPrefix.length() <= contextLength) { |
| return sharedPrefix; |
| } |
| return ELLIPSIS + sharedPrefix.substring(sharedPrefix.length() - contextLength); |
| } |
| |
| public String compactSuffix() { |
| if (sharedSuffix.length() <= contextLength) { |
| return sharedSuffix; |
| } |
| return sharedSuffix.substring(0, contextLength) + ELLIPSIS; |
| } |
| |
| private String extractDiff(String source) { |
| return DIFF_START + source.substring(sharedPrefix.length(), source.length() - sharedSuffix.length()) |
| + DIFF_END; |
| } |
| } |
| } |
| } |