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