001: package org.junit;
002:
003: /**
004: * Thrown when an {@link org.junit.Assert#assertEquals(Object, Object) assertEquals(String, String)} fails. Create and throw
005: * a <code>ComparisonFailure</code> manually if you want to show users the difference between two complex
006: * strings.
007: *
008: * Inspired by a patch from Alex Chaffee (alex@purpletech.com)
009: */
010: public class ComparisonFailure extends AssertionError {
011: /**
012: * The maximum length for fExpected and fActual. If it is exceeded, the strings should be shortened.
013: * @see ComparisonCompactor
014: */
015: private static final int MAX_CONTEXT_LENGTH = 20;
016: private static final long serialVersionUID = 1L;
017:
018: private String fExpected;
019: private String fActual;
020:
021: /**
022: * Constructs a comparison failure.
023: * @param message the identifying message or null
024: * @param expected the expected string value
025: * @param actual the actual string value
026: */
027: public ComparisonFailure(String message, String expected,
028: String actual) {
029: super (message);
030: fExpected = expected;
031: fActual = actual;
032: }
033:
034: /**
035: * Returns "..." in place of common prefix and "..." in
036: * place of common suffix between expected and actual.
037: *
038: * @see Throwable#getMessage()
039: */
040: @Override
041: public String getMessage() {
042: return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected,
043: fActual).compact(super .getMessage());
044: }
045:
046: /**
047: * Returns the actual string value
048: * @return the actual string value
049: */
050: public String getActual() {
051: return fActual;
052: }
053:
054: /**
055: * Returns the expected string value
056: * @return the expected string value
057: */
058: public String getExpected() {
059: return fExpected;
060: }
061:
062: private static class ComparisonCompactor {
063: private static final String ELLIPSIS = "...";
064: private static final String DELTA_END = "]";
065: private static final String DELTA_START = "[";
066:
067: /**
068: * The maximum length for <code>expected</code> and <code>actual</code>. When <code>contextLength</code>
069: * is exceeded, the Strings are shortened
070: */
071: private int fContextLength;
072: private String fExpected;
073: private String fActual;
074: private int fPrefix;
075: private int fSuffix;
076:
077: /**
078: * @param contextLength the maximum length for <code>expected</code> and <code>actual</code>. When contextLength
079: * is exceeded, the Strings are shortened
080: * @param expected the expected string value
081: * @param actual the actual string value
082: */
083: public ComparisonCompactor(int contextLength, String expected,
084: String actual) {
085: fContextLength = contextLength;
086: fExpected = expected;
087: fActual = actual;
088: }
089:
090: public String compact(String message) {
091: if (fExpected == null || fActual == null
092: || areStringsEqual())
093: return Assert.format(message, fExpected, fActual);
094:
095: findCommonPrefix();
096: findCommonSuffix();
097: String expected = compactString(fExpected);
098: String actual = compactString(fActual);
099: return Assert.format(message, expected, actual);
100: }
101:
102: private String compactString(String source) {
103: String result = DELTA_START
104: + source.substring(fPrefix, source.length()
105: - fSuffix + 1) + DELTA_END;
106: if (fPrefix > 0)
107: result = computeCommonPrefix() + result;
108: if (fSuffix > 0)
109: result = result + computeCommonSuffix();
110: return result;
111: }
112:
113: private void findCommonPrefix() {
114: fPrefix = 0;
115: int end = Math.min(fExpected.length(), fActual.length());
116: for (; fPrefix < end; fPrefix++) {
117: if (fExpected.charAt(fPrefix) != fActual
118: .charAt(fPrefix))
119: break;
120: }
121: }
122:
123: private void findCommonSuffix() {
124: int expectedSuffix = fExpected.length() - 1;
125: int actualSuffix = fActual.length() - 1;
126: for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
127: if (fExpected.charAt(expectedSuffix) != fActual
128: .charAt(actualSuffix))
129: break;
130: }
131: fSuffix = fExpected.length() - expectedSuffix;
132: }
133:
134: private String computeCommonPrefix() {
135: return (fPrefix > fContextLength ? ELLIPSIS : "")
136: + fExpected.substring(Math.max(0, fPrefix
137: - fContextLength), fPrefix);
138: }
139:
140: private String computeCommonSuffix() {
141: int end = Math.min(fExpected.length() - fSuffix + 1
142: + fContextLength, fExpected.length());
143: return fExpected.substring(
144: fExpected.length() - fSuffix + 1, end)
145: + (fExpected.length() - fSuffix + 1 < fExpected
146: .length()
147: - fContextLength ? ELLIPSIS : "");
148: }
149:
150: private boolean areStringsEqual() {
151: return fExpected.equals(fActual);
152: }
153: }
154: }
|