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;
}
}
}
}