001 /* 002 * Created on Aug 4, 2010 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 005 * the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 011 * specific language governing permissions and limitations under the License. 012 * 013 * Copyright @2010-2011 the original author or authors. 014 */ 015 package org.fest.assertions.internal; 016 017 import static org.fest.assertions.error.ShouldBeEqual.shouldBeEqual; 018 import static org.fest.assertions.error.ShouldBeIn.shouldBeIn; 019 import static org.fest.assertions.error.ShouldBeInstance.shouldBeInstance; 020 import static org.fest.assertions.error.ShouldBeInstanceOfAny.shouldBeInstanceOfAny; 021 import static org.fest.assertions.error.ShouldBeLenientEqualByAccepting.shouldBeLenientEqualByAccepting; 022 import static org.fest.assertions.error.ShouldBeLenientEqualByIgnoring.shouldBeLenientEqualByIgnoring; 023 import static org.fest.assertions.error.ShouldBeSame.shouldBeSame; 024 import static org.fest.assertions.error.ShouldNotBeEqual.shouldNotBeEqual; 025 import static org.fest.assertions.error.ShouldNotBeIn.shouldNotBeIn; 026 import static org.fest.assertions.error.ShouldNotBeNull.shouldNotBeNull; 027 import static org.fest.assertions.error.ShouldNotBeSame.shouldNotBeSame; 028 import static org.fest.util.Collections.*; 029 import static org.fest.util.ToString.toStringOf; 030 031 import java.lang.reflect.Field; 032 import java.util.Comparator; 033 import java.util.LinkedList; 034 import java.util.List; 035 import java.util.Set; 036 037 import org.fest.assertions.core.AssertionInfo; 038 import org.fest.util.ComparatorBasedComparisonStrategy; 039 import org.fest.util.ComparisonStrategy; 040 import org.fest.util.IntrospectionError; 041 import org.fest.util.StandardComparisonStrategy; 042 import org.fest.util.VisibleForTesting; 043 044 /** 045 * Reusable assertions for {@code Object}s. 046 * 047 * @author Yvonne Wang 048 * @author Alex Ruiz 049 * @author Nicolas François 050 */ 051 public class Objects { 052 053 private static final Objects INSTANCE = new Objects(); 054 055 /** 056 * Returns the singleton instance of this class based on {@link StandardComparisonStrategy}. 057 * @return the singleton instance of this class based on {@link StandardComparisonStrategy}. 058 */ 059 public static Objects instance() { 060 return INSTANCE; 061 } 062 063 @VisibleForTesting 064 Failures failures = Failures.instance(); 065 066 @VisibleForTesting 067 PropertySupport propertySupport = PropertySupport.instance(); 068 069 private final ComparisonStrategy comparisonStrategy; 070 071 @VisibleForTesting 072 Objects() { 073 this(StandardComparisonStrategy.instance()); 074 } 075 076 public Objects(ComparisonStrategy comparisonStrategy) { 077 this.comparisonStrategy = comparisonStrategy; 078 } 079 080 @VisibleForTesting 081 public Comparator<?> getComparator() { 082 if (comparisonStrategy instanceof ComparatorBasedComparisonStrategy) { 083 return ((ComparatorBasedComparisonStrategy)comparisonStrategy).getComparator(); 084 } 085 return null; 086 } 087 088 /** 089 * Verifies that the given object is an instance of the given type. 090 * @param info contains information about the assertion. 091 * @param actual the given object. 092 * @param type the type to check the given object against. 093 * @throws NullPointerException if the given type is {@code null}. 094 * @throws AssertionError if the given object is {@code null}. 095 * @throws AssertionError if the given object is not an instance of the given type. 096 */ 097 public void assertIsInstanceOf(AssertionInfo info, Object actual, Class<?> type) { 098 if (type == null) throw new NullPointerException("The given type should not be null"); 099 assertNotNull(info, actual); 100 if (type.isInstance(actual)) return; 101 throw failures.failure(info, shouldBeInstance(actual, type)); 102 } 103 104 /** 105 * Verifies that the given object is an instance of any of the given types. 106 * @param info contains information about the assertion. 107 * @param actual the given object. 108 * @param types the types to check the given object against. 109 * @throws NullPointerException if the given array is {@code null}. 110 * @throws IllegalArgumentException if the given array is empty. 111 * @throws NullPointerException if the given array has {@code null} elements. 112 * @throws AssertionError if the given object is {@code null}. 113 * @throws AssertionError if the given object is not an instance of any of the given types. 114 */ 115 public void assertIsInstanceOfAny(AssertionInfo info, Object actual, Class<?>[] types) { 116 checkIsNotNullAndIsNotEmpty(types); 117 assertNotNull(info, actual); 118 boolean found = false; 119 for (Class<?> type : types) { 120 if (type == null) { 121 String format = "The given array of types:<%s> should not have null elements"; 122 throw new NullPointerException(String.format(format, toStringOf(types))); 123 } 124 if (type.isInstance(actual)) { 125 found = true; 126 break; 127 } 128 } 129 if (found) return; 130 throw failures.failure(info, shouldBeInstanceOfAny(actual, types)); 131 } 132 133 private void checkIsNotNullAndIsNotEmpty(Class<?>[] types) { 134 if (types == null) throw new NullPointerException("The given array of types should not be null"); 135 if (types.length == 0) throw new IllegalArgumentException("The given array of types should not be empty"); 136 } 137 138 /** 139 * Asserts that two objects are equal. 140 * @param info contains information about the assertion. 141 * @param actual the "actual" object. 142 * @param expected the "expected" object. 143 * @throws AssertionError if {@code actual} is not equal to {@code expected}. This method will throw a 144 * {@code org.junit.ComparisonFailure} instead if JUnit is in the classpath and the given objects are not 145 * equal. 146 */ 147 public void assertEqual(AssertionInfo info, Object actual, Object expected) { 148 if (areEqual(actual, expected)) return; 149 throw failures.failure(info, shouldBeEqual(actual, expected, comparisonStrategy)); 150 } 151 152 /** 153 * Asserts that two objects are not equal. 154 * @param info contains information about the assertion. 155 * @param actual the given object. 156 * @param other the object to compare {@code actual} to. 157 * @throws AssertionError if {@code actual} is equal to {@code other}. 158 */ 159 public void assertNotEqual(AssertionInfo info, Object actual, Object other) { 160 if (!areEqual(actual, other)) return; 161 throw failures.failure(info, shouldNotBeEqual(actual, other, comparisonStrategy)); 162 } 163 164 /** 165 * Compares actual and other with standard strategy (null safe equals check). 166 * @param actual the object to compare to other 167 * @param other the object to compare to actual 168 * @return true if actual and other are equal (null safe equals check), false otherwise. 169 */ 170 private boolean areEqual(Object actual, Object other) { 171 return comparisonStrategy.areEqual(other, actual); 172 } 173 174 /** 175 * Asserts that the given object is {@code null}. 176 * @param info contains information about the assertion. 177 * @param actual the given object. 178 * @throws AssertionError if the given object is not {@code null}. 179 */ 180 public void assertNull(AssertionInfo info, Object actual) { 181 if (actual == null) return; 182 throw failures.failure(info, shouldBeEqual(actual, null, comparisonStrategy)); 183 } 184 185 /** 186 * Asserts that the given object is not {@code null}. 187 * @param info contains information about the assertion. 188 * @param actual the given object. 189 * @throws AssertionError if the given object is {@code null}. 190 */ 191 public void assertNotNull(AssertionInfo info, Object actual) { 192 if (actual != null) return; 193 throw failures.failure(info, shouldNotBeNull()); 194 } 195 196 /** 197 * Asserts that two objects refer to the same object. 198 * @param info contains information about the assertion. 199 * @param actual the given object. 200 * @param expected the expected object. 201 * @throws AssertionError if the given objects do not refer to the same object. 202 */ 203 public void assertSame(AssertionInfo info, Object actual, Object expected) { 204 if (actual == expected) return; 205 throw failures.failure(info, shouldBeSame(actual, expected)); 206 } 207 208 /** 209 * Asserts that two objects do not refer to the same object. 210 * @param info contains information about the assertion. 211 * @param actual the given object. 212 * @param other the object to compare {@code actual} to. 213 * @throws AssertionError if the given objects refer to the same object. 214 */ 215 public void assertNotSame(AssertionInfo info, Object actual, Object other) { 216 if (actual != other) return; 217 throw failures.failure(info, shouldNotBeSame(actual)); 218 } 219 220 /** 221 * Asserts that the given object is present in the given array. 222 * @param info contains information about the assertion. 223 * @param actual the given object. 224 * @param values the given array. 225 * @throws NullPointerException if the given array is {@code null}. 226 * @throws IllegalArgumentException if the given array is empty. 227 * @throws AssertionError if the given object is not present in the given array. 228 */ 229 public void assertIsIn(AssertionInfo info, Object actual, Object[] values) { 230 checkIsNotNullAndNotEmpty(values); 231 assertNotNull(info, actual); 232 if (isItemInArray(actual, values)) return; 233 throw failures.failure(info, shouldBeIn(actual, values, comparisonStrategy)); 234 } 235 236 /** 237 * Asserts that the given object is not present in the given array. 238 * @param info contains information about the assertion. 239 * @param actual the given object. 240 * @param values the given array. 241 * @throws NullPointerException if the given array is {@code null}. 242 * @throws IllegalArgumentException if the given array is empty. 243 * @throws AssertionError if the given object is present in the given array. 244 */ 245 public void assertIsNotIn(AssertionInfo info, Object actual, Object[] values) { 246 checkIsNotNullAndNotEmpty(values); 247 assertNotNull(info, actual); 248 if (!isItemInArray(actual, values)) return; 249 throw failures.failure(info, shouldNotBeIn(actual, values, comparisonStrategy)); 250 } 251 252 private void checkIsNotNullAndNotEmpty(Object[] values) { 253 if (values == null) throw new NullPointerException("The given array should not be null"); 254 if (values.length == 0) throw new IllegalArgumentException("The given array should not be empty"); 255 } 256 257 /** 258 * Returns <code>true</code> if given item is in given array, <code>false</code> otherwise. 259 * @param item the object to look for in arrayOfValues 260 * @param arrayOfValues the array of values 261 * @return <code>true</code> if given item is in given array, <code>false</code> otherwise. 262 */ 263 private boolean isItemInArray(Object item, Object[] arrayOfValues) { 264 for (Object value : arrayOfValues) 265 if (areEqual(value, item)) return true; 266 return false; 267 } 268 269 /** 270 * Asserts that the given object is present in the given collection. 271 * @param info contains information about the assertion. 272 * @param actual the given object. 273 * @param values the given iterable. 274 * @throws NullPointerException if the given collection is {@code null}. 275 * @throws IllegalArgumentException if the given collection is empty. 276 * @throws AssertionError if the given object is not present in the given collection. 277 */ 278 public <A> void assertIsIn(AssertionInfo info, Object actual, Iterable<? extends A> values) { 279 checkIsNotNullAndNotEmpty(values); 280 assertNotNull(info, actual); 281 if (isActualIn(actual, values)) return; 282 throw failures.failure(info, shouldBeIn(actual, values, comparisonStrategy)); 283 } 284 285 /** 286 * Asserts that the given object is not present in the given collection. 287 * @param info contains information about the assertion. 288 * @param actual the given object. 289 * @param values the given collection. 290 * @throws NullPointerException if the given iterable is {@code null}. 291 * @throws IllegalArgumentException if the given collection is empty. 292 * @throws AssertionError if the given object is present in the given collection. 293 */ 294 public <A> void assertIsNotIn(AssertionInfo info, Object actual, Iterable<? extends A> values) { 295 checkIsNotNullAndNotEmpty(values); 296 assertNotNull(info, actual); 297 if (!isActualIn(actual, values)) return; 298 throw failures.failure(info, shouldNotBeIn(actual, values, comparisonStrategy)); 299 } 300 301 private void checkIsNotNullAndNotEmpty(Iterable<?> values) { 302 if (values == null) throw new NullPointerException("The given iterable should not be null"); 303 if (!values.iterator().hasNext()) throw new IllegalArgumentException("The given iterable should not be empty"); 304 } 305 306 private <A> boolean isActualIn(Object actual, Iterable<? extends A> values) { 307 for (Object value : values) 308 if (areEqual(value, actual)) return true; 309 return false; 310 } 311 312 /** 313 * Assert that the given object is lenient equals by ignoring null fields value on other object. 314 * @param info contains information about the assertion. 315 * @param actual the given object. 316 * @param other the object to compare {@code actual} to. 317 * @throws NullPointerException if the actual type is {@code null}. 318 * @throws NullPointerException if the other type is {@code null}. 319 * @throws AssertionError if the actual and the given object are not lenient equals. 320 * @throws AssertionError if the other object is not an instance of the actual type. 321 */ 322 public void assertIsLenientEqualsToByIgnoringNullFields(AssertionInfo info, Object actual, Object other){ 323 assertIsInstanceOf(info, other, actual.getClass()); 324 List<String> fieldsNames = new LinkedList<String>(); 325 List<Object> values = new LinkedList<Object>(); 326 List<String> nullFields = new LinkedList<String>(); 327 for (Field field : actual.getClass().getDeclaredFields()) { 328 try { 329 Object otherFieldValue = propertySupport.propertyValue(field.getName(), other, field.getType()); 330 if(otherFieldValue != null){ 331 Object actualFieldValue = propertySupport.propertyValue(field.getName(), actual, field.getType()); 332 if(!otherFieldValue.equals(actualFieldValue)){ 333 fieldsNames.add(field.getName()); 334 values.add(otherFieldValue); 335 } 336 } else { 337 nullFields.add(field.getName()); 338 } 339 } catch (IntrospectionError e) { 340 // Not readeable field, skip. 341 } 342 } 343 if(fieldsNames.isEmpty()) return; 344 throw failures.failure(info, shouldBeLenientEqualByIgnoring (actual, fieldsNames, values, nullFields)); 345 } 346 347 /** 348 * Assert that the given object is lenient equals by ignoring null fields value on other object. 349 * @param info contains information about the assertion. 350 * @param actual the given object. 351 * @param other the object to compare {@code actual} to. 352 * @param fields accepted fields 353 * @throws NullPointerException if the actual type is {@code null}. 354 * @throws NullPointerException if the other type is {@code null}. 355 * @throws AssertionError if the actual and the given object are not lenient equals. 356 * @throws AssertionError if the other object is not an instance of the actual type. 357 * @throws IntrospectionError if a field does not exist in actual. 358 */ 359 public void assertIsLenientEqualsToByAcceptingFields(AssertionInfo info, Object actual, Object other, String... fields){ 360 assertIsInstanceOf(info, other, actual.getClass()); 361 List<String> fieldsNames = new LinkedList<String>(); 362 List<Object> values = new LinkedList<Object>(); 363 for (String fieldName : fields) { 364 Object actualFieldValue = propertySupport.propertyValue(fieldName, actual, Object.class); 365 Object otherFieldValue = propertySupport.propertyValue(fieldName, other, Object.class); 366 if(!(actualFieldValue == otherFieldValue || (actualFieldValue != null && actualFieldValue.equals(otherFieldValue)))){ 367 fieldsNames.add(fieldName); 368 values.add(otherFieldValue); 369 } 370 } 371 if(fieldsNames.isEmpty()) return; 372 throw failures.failure(info, shouldBeLenientEqualByAccepting(actual, fieldsNames, values, list(fields))); 373 } 374 375 /** 376 * Assert that the given object is lenient equals by ignoring fields. 377 * @param info contains information about the assertion. 378 * @param actual the given object. 379 * @param other the object to compare {@code actual} to. 380 * @param fields ignore fields 381 * @throws NullPointerException if the actual type is {@code null}. 382 * @throws NullPointerException if the other type is {@code null}. 383 * @throws AssertionError if the actual and the given object are not lenient equals. 384 * @throws AssertionError if the other object is not an instance of the actual type. 385 */ 386 public void assertIsLenientEqualsToByIgnoringFields(AssertionInfo info, Object actual, Object other, String... fields) { 387 assertIsInstanceOf(info, other, actual.getClass()); 388 List<String> fieldsNames = new LinkedList<String>(); 389 List<Object> values = new LinkedList<Object>(); 390 Set<String> ignoredFields = set(fields); 391 for (Field field : actual.getClass().getDeclaredFields()) { 392 try { 393 if(!ignoredFields.contains(field.getName())){ 394 Object otherFieldValue = propertySupport.propertyValue(field.getName(), other, field.getType()); 395 if(otherFieldValue != null){ 396 Object actualFieldValue = propertySupport.propertyValue(field.getName(), actual, field.getType()); 397 if(!otherFieldValue.equals(actualFieldValue)){ 398 fieldsNames.add(field.getName()); 399 values.add(otherFieldValue); 400 } 401 } 402 } 403 } catch (IntrospectionError e) { 404 // Not readeable field, skip. 405 } 406 } 407 if(fieldsNames.isEmpty()) return; 408 throw failures.failure(info,shouldBeLenientEqualByIgnoring(actual, fieldsNames, values, list(fields))); 409 } 410 }