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 }