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 * @author Mikhail Mazursky 051 */ 052 public class Objects { 053 054 private static final Objects INSTANCE = new Objects(); 055 056 /** 057 * Returns the singleton instance of this class based on {@link StandardComparisonStrategy}. 058 * @return the singleton instance of this class based on {@link StandardComparisonStrategy}. 059 */ 060 public static Objects instance() { 061 return INSTANCE; 062 } 063 064 @VisibleForTesting 065 Failures failures = Failures.instance(); 066 067 @VisibleForTesting 068 PropertySupport propertySupport = PropertySupport.instance(); 069 070 private final ComparisonStrategy comparisonStrategy; 071 072 @VisibleForTesting 073 Objects() { 074 this(StandardComparisonStrategy.instance()); 075 } 076 077 public Objects(ComparisonStrategy comparisonStrategy) { 078 this.comparisonStrategy = comparisonStrategy; 079 } 080 081 @VisibleForTesting 082 public Comparator<?> getComparator() { 083 if (comparisonStrategy instanceof ComparatorBasedComparisonStrategy) { 084 return ((ComparatorBasedComparisonStrategy)comparisonStrategy).getComparator(); 085 } 086 return null; 087 } 088 089 /** 090 * Verifies that the given object is an instance of the given type. 091 * @param info contains information about the assertion. 092 * @param actual the given object. 093 * @param type the type to check the given object against. 094 * @throws NullPointerException if the given type is {@code null}. 095 * @throws AssertionError if the given object is {@code null}. 096 * @throws AssertionError if the given object is not an instance of the given type. 097 */ 098 public void assertIsInstanceOf(AssertionInfo info, Object actual, Class<?> type) { 099 if (type == null) throw new NullPointerException("The given type should not be null"); 100 assertNotNull(info, actual); 101 if (type.isInstance(actual)) return; 102 throw failures.failure(info, shouldBeInstance(actual, type)); 103 } 104 105 /** 106 * Verifies that the given object is an instance of any of the given types. 107 * @param info contains information about the assertion. 108 * @param actual the given object. 109 * @param types the types to check the given object against. 110 * @throws NullPointerException if the given array is {@code null}. 111 * @throws IllegalArgumentException if the given array is empty. 112 * @throws NullPointerException if the given array has {@code null} elements. 113 * @throws AssertionError if the given object is {@code null}. 114 * @throws AssertionError if the given object is not an instance of any of the given types. 115 */ 116 public void assertIsInstanceOfAny(AssertionInfo info, Object actual, Class<?>[] types) { 117 checkIsNotNullAndIsNotEmpty(types); 118 assertNotNull(info, actual); 119 boolean found = false; 120 for (Class<?> type : types) { 121 if (type == null) { 122 String format = "The given array of types:<%s> should not have null elements"; 123 throw new NullPointerException(String.format(format, toStringOf(types))); 124 } 125 if (type.isInstance(actual)) { 126 found = true; 127 break; 128 } 129 } 130 if (found) return; 131 throw failures.failure(info, shouldBeInstanceOfAny(actual, types)); 132 } 133 134 private void checkIsNotNullAndIsNotEmpty(Class<?>[] types) { 135 if (types == null) throw new NullPointerException("The given array of types should not be null"); 136 if (types.length == 0) throw new IllegalArgumentException("The given array of types should not be empty"); 137 } 138 139 /** 140 * Asserts that two objects are equal. 141 * @param info contains information about the assertion. 142 * @param actual the "actual" object. 143 * @param expected the "expected" object. 144 * @throws AssertionError if {@code actual} is not equal to {@code expected}. This method will throw a 145 * {@code org.junit.ComparisonFailure} instead if JUnit is in the classpath and the given objects are not 146 * equal. 147 */ 148 public void assertEqual(AssertionInfo info, Object actual, Object expected) { 149 if (areEqual(actual, expected)) return; 150 throw failures.failure(info, shouldBeEqual(actual, expected, comparisonStrategy)); 151 } 152 153 /** 154 * Asserts that two objects are not equal. 155 * @param info contains information about the assertion. 156 * @param actual the given object. 157 * @param other the object to compare {@code actual} to. 158 * @throws AssertionError if {@code actual} is equal to {@code other}. 159 */ 160 public void assertNotEqual(AssertionInfo info, Object actual, Object other) { 161 if (!areEqual(actual, other)) return; 162 throw failures.failure(info, shouldNotBeEqual(actual, other, comparisonStrategy)); 163 } 164 165 /** 166 * Compares actual and other with standard strategy (null safe equals check). 167 * @param actual the object to compare to other 168 * @param other the object to compare to actual 169 * @return true if actual and other are equal (null safe equals check), false otherwise. 170 */ 171 private boolean areEqual(Object actual, Object other) { 172 return comparisonStrategy.areEqual(other, actual); 173 } 174 175 /** 176 * Asserts that the given object is {@code null}. 177 * @param info contains information about the assertion. 178 * @param actual the given object. 179 * @throws AssertionError if the given object is not {@code null}. 180 */ 181 public void assertNull(AssertionInfo info, Object actual) { 182 if (actual == null) return; 183 throw failures.failure(info, shouldBeEqual(actual, null, comparisonStrategy)); 184 } 185 186 /** 187 * Asserts that the given object is not {@code null}. 188 * @param info contains information about the assertion. 189 * @param actual the given object. 190 * @throws AssertionError if the given object is {@code null}. 191 */ 192 public void assertNotNull(AssertionInfo info, Object actual) { 193 if (actual != null) return; 194 throw failures.failure(info, shouldNotBeNull()); 195 } 196 197 /** 198 * Asserts that two objects refer to the same object. 199 * @param info contains information about the assertion. 200 * @param actual the given object. 201 * @param expected the expected object. 202 * @throws AssertionError if the given objects do not refer to the same object. 203 */ 204 public void assertSame(AssertionInfo info, Object actual, Object expected) { 205 if (actual == expected) return; 206 throw failures.failure(info, shouldBeSame(actual, expected)); 207 } 208 209 /** 210 * Asserts that two objects do not refer to the same object. 211 * @param info contains information about the assertion. 212 * @param actual the given object. 213 * @param other the object to compare {@code actual} to. 214 * @throws AssertionError if the given objects refer to the same object. 215 */ 216 public void assertNotSame(AssertionInfo info, Object actual, Object other) { 217 if (actual != other) return; 218 throw failures.failure(info, shouldNotBeSame(actual)); 219 } 220 221 /** 222 * Asserts that the given object is present in the given array. 223 * @param info contains information about the assertion. 224 * @param actual the given object. 225 * @param values the given array. 226 * @throws NullPointerException if the given array is {@code null}. 227 * @throws IllegalArgumentException if the given array is empty. 228 * @throws AssertionError if the given object is not present in the given array. 229 */ 230 public void assertIsIn(AssertionInfo info, Object actual, Object[] values) { 231 checkIsNotNullAndNotEmpty(values); 232 assertNotNull(info, actual); 233 if (isItemInArray(actual, values)) return; 234 throw failures.failure(info, shouldBeIn(actual, values, comparisonStrategy)); 235 } 236 237 /** 238 * Asserts that the given object is not present in the given array. 239 * @param info contains information about the assertion. 240 * @param actual the given object. 241 * @param values the given array. 242 * @throws NullPointerException if the given array is {@code null}. 243 * @throws IllegalArgumentException if the given array is empty. 244 * @throws AssertionError if the given object is present in the given array. 245 */ 246 public void assertIsNotIn(AssertionInfo info, Object actual, Object[] values) { 247 checkIsNotNullAndNotEmpty(values); 248 assertNotNull(info, actual); 249 if (!isItemInArray(actual, values)) return; 250 throw failures.failure(info, shouldNotBeIn(actual, values, comparisonStrategy)); 251 } 252 253 private void checkIsNotNullAndNotEmpty(Object[] values) { 254 if (values == null) throw new NullPointerException("The given array should not be null"); 255 if (values.length == 0) throw new IllegalArgumentException("The given array should not be empty"); 256 } 257 258 /** 259 * Returns <code>true</code> if given item is in given array, <code>false</code> otherwise. 260 * @param item the object to look for in arrayOfValues 261 * @param arrayOfValues the array of values 262 * @return <code>true</code> if given item is in given array, <code>false</code> otherwise. 263 */ 264 private boolean isItemInArray(Object item, Object[] arrayOfValues) { 265 for (Object value : arrayOfValues) 266 if (areEqual(value, item)) return true; 267 return false; 268 } 269 270 /** 271 * Asserts that the given object is present in the given collection. 272 * @param info contains information about the assertion. 273 * @param actual the given object. 274 * @param values the given iterable. 275 * @throws NullPointerException if the given collection is {@code null}. 276 * @throws IllegalArgumentException if the given collection is empty. 277 * @throws AssertionError if the given object is not present in the given collection. 278 */ 279 public <A> void assertIsIn(AssertionInfo info, A actual, Iterable<? extends A> values) { 280 checkIsNotNullAndNotEmpty(values); 281 assertNotNull(info, actual); 282 if (isActualIn(actual, values)) return; 283 throw failures.failure(info, shouldBeIn(actual, values, comparisonStrategy)); 284 } 285 286 /** 287 * Asserts that the given object is not present in the given collection. 288 * @param info contains information about the assertion. 289 * @param actual the given object. 290 * @param values the given collection. 291 * @throws NullPointerException if the given iterable is {@code null}. 292 * @throws IllegalArgumentException if the given collection is empty. 293 * @throws AssertionError if the given object is present in the given collection. 294 */ 295 public <A> void assertIsNotIn(AssertionInfo info, A actual, Iterable<? extends A> values) { 296 checkIsNotNullAndNotEmpty(values); 297 assertNotNull(info, actual); 298 if (!isActualIn(actual, values)) return; 299 throw failures.failure(info, shouldNotBeIn(actual, values, comparisonStrategy)); 300 } 301 302 private void checkIsNotNullAndNotEmpty(Iterable<?> values) { 303 if (values == null) throw new NullPointerException("The given iterable should not be null"); 304 if (!values.iterator().hasNext()) throw new IllegalArgumentException("The given iterable should not be empty"); 305 } 306 307 private <A> boolean isActualIn(A actual, Iterable<? extends A> values) { 308 for (A value : values) 309 if (areEqual(value, actual)) return true; 310 return false; 311 } 312 313 /** 314 * Assert that the given object is lenient equals by ignoring null fields value on other object. 315 * @param info contains information about the assertion. 316 * @param actual the given object. 317 * @param other the object to compare {@code actual} to. 318 * @throws NullPointerException if the actual type is {@code null}. 319 * @throws NullPointerException if the other type is {@code null}. 320 * @throws AssertionError if the actual and the given object are not lenient equals. 321 * @throws AssertionError if the other object is not an instance of the actual type. 322 */ 323 public <A> void assertIsLenientEqualsToByIgnoringNullFields(AssertionInfo info, A actual, A other){ 324 assertIsInstanceOf(info, other, actual.getClass()); 325 List<String> fieldsNames = new LinkedList<String>(); 326 List<Object> values = new LinkedList<Object>(); 327 List<String> nullFields = new LinkedList<String>(); 328 for (Field field : actual.getClass().getDeclaredFields()) { 329 try { 330 Object otherFieldValue = propertySupport.propertyValue(field.getName(), field.getType(), other); 331 if (otherFieldValue != null) { 332 Object actualFieldValue = propertySupport.propertyValue(field.getName(), field.getType(), actual); 333 if (!otherFieldValue.equals(actualFieldValue)){ 334 fieldsNames.add(field.getName()); 335 values.add(otherFieldValue); 336 } 337 } else { 338 nullFields.add(field.getName()); 339 } 340 } catch (IntrospectionError e) { 341 // Not readeable field, skip. 342 } 343 } 344 if (fieldsNames.isEmpty()) return; 345 throw failures.failure(info, shouldBeLenientEqualByIgnoring (actual, fieldsNames, values, nullFields)); 346 } 347 348 /** 349 * Assert that the given object is lenient equals by ignoring null fields value on other object. 350 * @param info contains information about the assertion. 351 * @param actual the given object. 352 * @param other the object to compare {@code actual} to. 353 * @param fields accepted fields 354 * @throws NullPointerException if the actual type is {@code null}. 355 * @throws NullPointerException if the other type is {@code null}. 356 * @throws AssertionError if the actual and the given object are not lenient equals. 357 * @throws AssertionError if the other object is not an instance of the actual type. 358 * @throws IntrospectionError if a field does not exist in actual. 359 */ 360 public <A> void assertIsLenientEqualsToByAcceptingFields(AssertionInfo info, A actual, A other, String... fields){ 361 assertIsInstanceOf(info, other, actual.getClass()); 362 List<String> fieldsNames = new LinkedList<String>(); 363 List<Object> values = new LinkedList<Object>(); 364 for (String fieldName : fields) { 365 Object actualFieldValue = propertySupport.propertyValue(fieldName, Object.class, actual); 366 Object otherFieldValue = propertySupport.propertyValue(fieldName, Object.class, other); 367 if (!(actualFieldValue == otherFieldValue || (actualFieldValue != null && actualFieldValue.equals(otherFieldValue)))) { 368 fieldsNames.add(fieldName); 369 values.add(otherFieldValue); 370 } 371 } 372 if (fieldsNames.isEmpty()) return; 373 throw failures.failure(info, shouldBeLenientEqualByAccepting(actual, fieldsNames, values, list(fields))); 374 } 375 376 /** 377 * Assert that the given object is lenient equals by ignoring fields. 378 * @param info contains information about the assertion. 379 * @param actual the given object. 380 * @param other the object to compare {@code actual} to. 381 * @param fields ignore fields 382 * @throws NullPointerException if the actual type is {@code null}. 383 * @throws NullPointerException if the other type is {@code null}. 384 * @throws AssertionError if the actual and the given object are not lenient equals. 385 * @throws AssertionError if the other object is not an instance of the actual type. 386 */ 387 public <A> void assertIsLenientEqualsToByIgnoringFields(AssertionInfo info, A actual, A other, String... fields) { 388 assertIsInstanceOf(info, other, actual.getClass()); 389 List<String> fieldsNames = new LinkedList<String>(); 390 List<Object> values = new LinkedList<Object>(); 391 Set<String> ignoredFields = set(fields); 392 for (Field field : actual.getClass().getDeclaredFields()) { 393 try { 394 if (!ignoredFields.contains(field.getName())) { 395 Object otherFieldValue = propertySupport.propertyValue(field.getName(), field.getType(), other); 396 if (otherFieldValue != null) { 397 Object actualFieldValue = propertySupport.propertyValue(field.getName(), field.getType(), actual); 398 if (!otherFieldValue.equals(actualFieldValue)) { 399 fieldsNames.add(field.getName()); 400 values.add(otherFieldValue); 401 } 402 } 403 } 404 } catch (IntrospectionError e) { 405 // Not readeable field, skip. 406 } 407 } 408 if (fieldsNames.isEmpty()) return; 409 throw failures.failure(info,shouldBeLenientEqualByIgnoring(actual, fieldsNames, values, list(fields))); 410 } 411 }