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 }