1 /*
2 * The JUnit-addons Software License, Version 1.0
3 * (based on the Apache Software License, Version 1.1)
4 *
5 * Copyright (c) 2003 Vladimir R. Bossicard. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution, if
20 * any, must include the following acknowlegement:
21 * "This product includes software developed by Vladimir R.
22 * Bossicard as well as other contributors
23 * (http://junit-addons.sourceforge.net/)."
24 * Alternately, this acknowlegement may appear in the software itself,
25 * if and wherever such third-party acknowlegements normally appear.
26 *
27 * 4. The name "JUnit-addons" must not be used to endorse or promote
28 * products derived from this software without prior written
29 * permission. For written permission, please contact
30 * vbossica@users.sourceforge.net.
31 *
32 * 5. Products derived from this software may not be called "JUnit-addons"
33 * nor may "JUnit-addons" appear in their names without prior written
34 * permission of the project managers.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 * ======================================================================
49 *
50 * This software consists of voluntary contributions made by many
51 * individuals. For more information on the JUnit-addons Project, please
52 * see <http://junit-addons.sourceforge.net/>.
53 */
54
55 package junitx.framework;
56
57 import junit.framework.AssertionFailedError;
58
59 /**
60 * Thrown when an assert equals for Strings failed (class
61 * <tt>junitx.framework.Assert</tt>).
62 *
63 * <h4>Usage</h4>
64 * To use this new class, you'll have to invoke the <tt>assertEquals</tt> method
65 * (for String objects) of the <tt>junitx.framework.Assert</tt> class:
66 *
67 * <pre>
68 * junitx.framework.Assert.assertEquals(message, expected, actual);
69 * </pre>
70 *
71 * @version $Revision: 1.1 $ $Date: 2003/03/21 06:13:49 $
72 * @author <a href="mailto:vbossica@users.sourceforge.net">Vladimir R. Bossicard</a>
73 */
74 public class ComparisonFailure extends AssertionFailedError {
75
76 private static final int MINIMAL_LENGTH = 10;
77
78 private String expected;
79 private String actual;
80 private String message = "";
81
82 /**
83 * Constructs a <tt>ComparisonFailure</tt> object.
84 */
85 public ComparisonFailure(String message,
86 String expected,
87 String actual) {
88 super(message);
89 this.expected = expected;
90 this.actual = actual;
91 this.message = createMessage(super.getMessage(), this.expected, this.actual);
92 }
93
94 public String getMessage() {
95 return this.message;
96 }
97
98 /**
99 * Creates the message that is returned by the <tt>getMessage</tt> method.
100 * The message is optimized so that its 'actual' part does either:
101 * <ul>
102 * <li>represent a word (delimited by stopper characters)
103 * <li>contain 10 characters</li>
104 * </ul>
105 */
106 protected static String createMessage(String message,
107 String expected,
108 String actual) {
109 if ((expected == null || actual == null) ||
110 (actual.equals(expected))) {
111 return format(message, expected, actual, null);
112 }
113
114 int end = Math.min(expected.length(), actual.length());
115 int beginDiff = 0;
116 while ((beginDiff < end) &&
117 (expected.charAt(beginDiff) == actual.charAt(beginDiff))) {
118 beginDiff++;
119 }
120
121 int endDiffExp = expected.length() - 1;
122 int endDiffAct = actual.length() - 1;
123 while ((endDiffExp >= beginDiff) &&
124 (endDiffAct >= beginDiff) &&
125 (expected.charAt(endDiffExp) == actual.charAt(endDiffAct))) {
126 endDiffAct--;
127 endDiffExp--;
128 }
129
130 /* the delta is the difference between the actual and expected values.
131 * If a character was omitted in the actual string, a delta according
132 * to the expected string is calculated */
133 String delta = actual.substring(beginDiff, endDiffAct+1);
134 if (delta.equals("")) {
135 delta = expected.substring(beginDiff, endDiffExp+1);
136 }
137
138 /* Now that we have the the minimal and maximal positions, we have to
139 * 'expand' the actual values' delta into both directions until it means
140 * something (we have a minimal length or we have reached some
141 * boundaries). */
142 boolean optimized = false;
143 while (!optimized) {
144 if (endDiffAct - beginDiff < MINIMAL_LENGTH - 1) {
145 if (beginDiff > 0 && !isStopper(actual.charAt(beginDiff))) {
146 beginDiff--;
147 } else if ((endDiffAct < actual.length()-1) &&
148 !isStopper(actual.charAt(endDiffAct))) {
149 endDiffAct++;
150 endDiffExp++;
151 } else {
152 optimized = true;
153 }
154 } else {
155 optimized = true;
156 }
157 }
158
159 String expectedDisplay = expected.substring(beginDiff, endDiffExp + 1);
160 String actualDisplay = actual.substring(beginDiff, endDiffAct + 1);
161
162 // if the delta is obvious, delete it
163 if (delta.equals(actualDisplay)) {
164 delta = null;
165 }
166
167 if (beginDiff > 0) {
168 expectedDisplay = "..." + expectedDisplay;
169 actualDisplay = "..." + actualDisplay;
170 }
171 if (endDiffExp < expected.length() - 1) {
172 expectedDisplay = expectedDisplay + "...";
173 }
174 if (endDiffAct < actual.length() - 1) {
175 actualDisplay = actualDisplay + "...";
176 }
177
178 return format(message, expectedDisplay, actualDisplay, delta);
179 }
180
181 private static boolean isStopper(char input) {
182 return (input == ' ') ||
183 (input == ',') || (input == ';') ||
184 (input == '(') || (input == ')') ||
185 (input == '[') || (input == ']') ||
186 (input == '{') || (input == '}');
187 }
188
189 private static String format(String message,
190 String expected,
191 String actual,
192 String delta) {
193 String formatted = "";
194 if (message != null) {
195 formatted = message + " ";
196 }
197 if (delta == null) {
198 return formatted + "expected:<" + expected + "> but was:<" + actual + ">";
199 } else {
200 return formatted + "expected:<" + expected + "> but was:<" + actual + "> ['" + delta + "']";
201 }
202 }
203
204 }