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 }