001 /*
002 * Created on Mar 29, 2009
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 @2009-2011 the original author or authors.
014 */
015 package org.fest.assertions;
016
017 import static java.util.Collections.emptyList;
018 import static org.fest.assertions.Formatting.format;
019 import static org.fest.util.Collections.list;
020 import static org.fest.util.Objects.areEqual;
021
022 import java.util.*;
023
024 import org.fest.util.IntrospectionError;
025
026 /**
027 * Assertions for <code>{@link List}</code>s.
028 * <p>
029 * To create a new instance of this class invoke <code>{@link Assertions#assertThat(List)}</code>.
030 * </p>
031 *
032 * @author Alex Ruiz
033 * @author Yvonne Wang
034 *
035 * @since 1.1
036 */
037 public class ListAssert extends ObjectGroupAssert<ListAssert, List<?>> {
038
039 /**
040 * Creates a new </code>{@link ListAssert}</code>.
041 * @param actual the target to verify.
042 */
043 protected ListAssert(List<?> actual) {
044 super(ListAssert.class, actual);
045 }
046
047 /**
048 * Verifies that the actual <code>{@link List}</code> contains the given object at the given index.
049 * @param o the object to look for.
050 * @param index the index where the object should be stored in the actual {@code List}.
051 * @return this assertion object.
052 * @throws NullPointerException if the given <code>Index</code> is {@code null}.
053 * @throws IndexOutOfBoundsException if the value of the given <code>Index</code> is negative, or equal to or greater
054 * than the size of the actual {@code List}.
055 * @throws AssertionError if the given {@code List} does not contain the given object at the given index.
056 */
057 public ListAssert contains(Object o, Index index) {
058 if (index == null) throw new NullPointerException(formattedErrorMessage("The given index should not be null"));
059 isNotNull().isNotEmpty();
060 int indexValue = index.value();
061 int listSize = actualGroupSize();
062 if (indexValue < 0 || indexValue >= listSize) failIndexOutOfBounds(indexValue);
063 Object actualElement = actual.get(indexValue);
064 if (!areEqual(actualElement, o)) failElementNotFound(o, actualElement, indexValue);
065 return this;
066 }
067
068 private void failElementNotFound(Object e, Object a, int index) {
069 failIfCustomMessageIsSet();
070 fail(format("expecting <%s> at index <%s> but found <%s>", e, index, a));
071 }
072
073 private void failIndexOutOfBounds(int index) {
074 throw new IndexOutOfBoundsException(formattedErrorMessage(
075 format("The index <%s> should be greater than or equal to zero and less than %s", index, actualGroupSize())));
076 }
077
078 /**
079 * Verifies that the actual <code>{@link List}</code> contains the given sequence of objects, without any other
080 * objects between them.
081 * @param sequence the sequence of objects to look for.
082 * @return this assertion object.
083 * @throws AssertionError if the actual {@code List} is {@code null}.
084 * @throws AssertionError if the given array is {@code null}.
085 * @throws AssertionError if the actual {@code List} does not contain the given sequence of objects.
086 */
087 public ListAssert containsSequence(Object... sequence) {
088 isNotNull();
089 validateIsNotNull(sequence);
090 int sequenceSize = sequence.length;
091 if (sequenceSize == 0) return this;
092 int indexOfFirst = actual.indexOf(sequence[0]);
093 if (indexOfFirst == -1) failIfSequenceNotFound(sequence);
094 int listSize = actualGroupSize();
095 for (int i = 0; i < sequenceSize; i++) {
096 int actualIndex = indexOfFirst + i;
097 if (actualIndex > listSize - 1) failIfSequenceNotFound(sequence);
098 if (!areEqual(sequence[i], actual.get(actualIndex))) failIfSequenceNotFound(sequence);
099 }
100 return this;
101 }
102
103 private void failIfSequenceNotFound(Object[] notFound) {
104 failIfCustomMessageIsSet();
105 fail(format("list:<%s> does not contain the sequence:<%s>", actual, notFound));
106 }
107
108 /**
109 * Verifies that the actual <code>{@link List}</code> starts with the given sequence of objects, without any other
110 * objects between them. Same as <code>{@link #containsSequence}</code>, but verifies also that first given object is
111 * also first element of {@code List}.
112 * @param sequence the sequence of objects to look for.
113 * @return this assertion object.
114 * @throws AssertionError if the actual {@code List} is {@code null}.
115 * @throws AssertionError if the given array is {@code null}.
116 * @throws AssertionError if the actual {@code List} is not empty and with the given sequence of objects is
117 * empty.
118 * @throws AssertionError if the actual {@code List} does not start with the given sequence of objects.
119 */
120 public ListAssert startsWith(Object... sequence) {
121 isNotNull();
122 validateIsNotNull(sequence);
123 int sequenceSize = sequence.length;
124 int listSize = actualGroupSize();
125 if (sequenceSize == 0 && listSize == 0) return this;
126 if (sequenceSize == 0 && listSize != 0) failIfNotStartingWithSequence(sequence);
127 if (listSize < sequenceSize) failIfNotStartingWithSequence(sequence);
128 for (int i = 0; i < sequenceSize; i++)
129 if (!areEqual(sequence[i], actual.get(i))) failIfNotStartingWithSequence(sequence);
130 return this;
131 }
132
133 private void failIfNotStartingWithSequence(Object[] notFound) {
134 failIfCustomMessageIsSet();
135 fail(format("list:<%s> does not start with the sequence:<%s>", actual, notFound));
136 }
137
138 /**
139 * Verifies that the actual <code>{@link List}</code> ends with the given sequence of objects, without any other
140 * objects between them. Same as <code>{@link #containsSequence}</code>, but verifies also that last given object is
141 * also last element of {@code List}.
142 * @param sequence the sequence of objects to look for.
143 * @return this assertion object.
144 * @throws AssertionError if the actual {@code List} is {@code null}.
145 * @throws AssertionError if the given array is {@code null}.
146 * @throws AssertionError if the actual {@code List} is not empty and with the given sequence of objects is
147 * empty.
148 * @throws AssertionError if the actual {@code List} does not end with the given sequence of objects.
149 */
150 public ListAssert endsWith(Object... sequence) {
151 isNotNull();
152 validateIsNotNull(sequence);
153 int sequenceSize = sequence.length;
154 int listSize = actualGroupSize();
155 if (sequenceSize == 0 && listSize == 0) return this;
156 if (sequenceSize == 0 && listSize != 0) failIfNotEndingWithSequence(sequence);
157 if (listSize < sequenceSize) failIfNotEndingWithSequence(sequence);
158 for (int i = 0; i < sequenceSize; i++) {
159 int sequenceIndex = sequenceSize - 1 - i;
160 int listIndex = listSize - 1 - i;
161 if (!areEqual(sequence[sequenceIndex], actual.get(listIndex))) failIfNotEndingWithSequence(sequence);
162 }
163 return this;
164 }
165
166 private void failIfNotEndingWithSequence(Object[] notFound) {
167 failIfCustomMessageIsSet();
168 fail(format("list:<%s> does not end with the sequence:<%s>", actual, notFound));
169 }
170
171 /**
172 * Returns the number of elements in the actual <code>{@link List}</code>.
173 * @return the number of elements in the actual {@code List}.
174 * @throws AssertionError if the actual {@code List} is {@code null}.
175 */
176 @Override protected int actualGroupSize() {
177 isNotNull();
178 return actual.size();
179 }
180
181 /**
182 * Verifies that the actual <code>{@link List}</code> contains the given objects, in the same order. This method works
183 * just like <code>{@link #isEqualTo(List)}</code>, with the difference that internally the given array is converted
184 * to a {@code List}.
185 * @param objects the objects to look for.
186 * @return this assertion object.
187 * @throws AssertionError if the actual {@code List} is {@code null}.
188 * @throws NullPointerException if the given array is {@code null}.
189 * @throws AssertionError if the actual {@code List} does not contain the given objects.
190 */
191 public ListAssert containsExactly(Object... objects) {
192 validateIsNotNull(objects);
193 return isNotNull().isEqualTo(list(objects));
194 }
195
196 /**
197 * Creates a new instance of <code>{@link ListAssert}</code> whose target list contains the values of the given
198 * property name from the elements of this {@code ListAssert}'s list. Property access works with both simple
199 * properties like {@code Person.age} and nested properties {@code Person.father.age}.
200 * </p>
201 * <p>
202 * For example, let's say we have a list of {@code Person} objects and you want to verify their age:
203 * <pre>
204 * assertThat(persons).onProperty("age").containsOnly(25, 16, 44, 37); // simple property
205 * assertThat(persons).onProperty("father.age").containsOnly(55, 46, 74, 62); // nested property
206 * </p>
207 * @param propertyName the name of the property to extract values from the actual list to build a new
208 * {@code ListAssert}.
209 * @return a new {@code ListAssert} containing the values of the given property name from the elements of this
210 * {@code ListAssert}'s list.
211 * @throws AssertionError if the actual list is {@code null}.
212 * @throws IntrospectionError if an element in the given list does not have a matching property.
213 * @since 1.3
214 */
215 @Override public ListAssert onProperty(String propertyName) {
216 isNotNull();
217 if (actual.isEmpty()) return new ListAssert(emptyList());
218 return new ListAssert(PropertySupport.instance().propertyValues(propertyName, actual));
219 }
220
221 /** {@inheritDoc} */
222 @Override protected Set<Object> actualAsSet() {
223 return new LinkedHashSet<Object>(actual);
224 }
225
226 /** {@inheritDoc} */
227 @Override protected List<Object> actualAsList() {
228 return new ArrayList<Object>(actual);
229 }
230 }