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. * * Inspired by a patch from Alex Chaffee (alex@purpletech.com) * * @since 4.0 */ public class ComparisonFailure extends AssertionError { /** * The maximum length for fExpected and fActual. 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; 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); fExpected = expected; 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 DELTA_END = "]"; private static final String DELTA_START = "["; /** * The maximum length for <code>expected</code> and <code>actual</code>. When <code>contextLength</code> * is exceeded, the Strings are shortened */ private int fContextLength; private String fExpected; private String fActual; private int fPrefix; private int fSuffix; /** * @param contextLength the maximum length for <code>expected</code> and <code>actual</code>. When contextLength * is exceeded, the Strings are shortened * @param expected the expected string value * @param actual the actual string value */ public ComparisonCompactor(int contextLength, String expected, String actual) { fContextLength = contextLength; fExpected = expected; fActual = actual; } private String compact(String message) { if (fExpected == null || fActual == null || areStringsEqual()) { return Assert.format(message, fExpected, fActual); } findCommonPrefix(); findCommonSuffix(); String expected = compactString(fExpected); String actual = compactString(fActual); return Assert.format(message, expected, actual); } private String compactString(String source) { String result = DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END; if (fPrefix > 0) { result = computeCommonPrefix() + result; } if (fSuffix > 0) { result = result + computeCommonSuffix(); } return result; } private void findCommonPrefix() { fPrefix = 0; int end = Math.min(fExpected.length(), fActual.length()); for (; fPrefix < end; fPrefix++) { if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) { break; } } } private void findCommonSuffix() { int expectedSuffix = fExpected.length() - 1; int actualSuffix = fActual.length() - 1; for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) { if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) { break; } } fSuffix = fExpected.length() - expectedSuffix; } private String computeCommonPrefix() { return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix); } private String computeCommonSuffix() { int end = Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length()); return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : ""); } private boolean areStringsEqual() { return fExpected.equals(fActual); } } }