001 // Copyright 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // 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
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.test;
016
017 import java.lang.reflect.Field;
018 import java.util.ArrayList;
019 import java.util.Arrays;
020 import java.util.List;
021
022 import org.testng.Assert;
023
024 /**
025 * Extra assertions on top of the standard set, packaged as a base class for easy referencing in tests. Also,
026 * utilities for instantiation objects and setting and reading private fields of those objects.
027 * <p>
028 * This class was originally in the tapestry-ioc module as was moved to tapestry-test; the package name was not changed
029 * to ensure backwards compatibility.
030 *
031 * @since 5.2.0
032 */
033 public class TestUtils extends Assert
034 {
035
036 /**
037 * Invoked from code that should not be reachable. For example, place a call to unreachable() after invoking a
038 * method that is expected to throw an exception.
039 */
040 public static void unreachable()
041 {
042 fail("This code should not be reachable.");
043 }
044
045 /**
046 * Asserts that the message property of the throwable contains each of the provided substrings.
047 *
048 * @param t
049 * throwable to check
050 * @param substrings
051 * some number of expected substrings
052 */
053 public static void assertMessageContains(Throwable t, String... substrings)
054 {
055 String message = t.getMessage();
056
057 for (String substring : substrings)
058 assertTrue(message.contains(substring), String.format("String '%s' not found in '%s'.", substring, message));
059 }
060
061 /**
062 * Compares two lists for equality; first all the elements are individually compared for equality (if the lists are
063 * of unequal length, only elements up to the shorter length are compared). Then the length of the lists are
064 * compared. This generally gives
065 *
066 * @param <T>
067 * type of objects to compare
068 * @param actual
069 * actual values to check
070 * @param expected
071 * expected values
072 */
073 public static <T> void assertListsEquals(List<T> actual, List<T> expected)
074 {
075 int count = Math.min(actual.size(), expected.size());
076
077 try
078 {
079 for (int i = 0; i < count; i++)
080 {
081 assertEquals(actual.get(i), expected.get(i), String.format("Element #%d.", i));
082 }
083
084 assertEquals(actual.size(), expected.size(), "List size.");
085 }
086 catch (AssertionError ae)
087 {
088 showLists(actual, expected);
089
090 throw ae;
091 }
092 }
093
094 protected static <T> void showLists(List<T> actual, List<T> expected)
095 {
096 List<String> actualStrings = toStrings(actual);
097 List<String> expectedStrings = toStrings(expected);
098
099 String format = String
100 .format("%%3d: [%%-%ds] [%%-%ds]\n", maxLength(actualStrings), maxLength(expectedStrings));
101
102 int count = Math.max(actual.size(), expected.size());
103
104 System.out.flush();
105 System.err.flush();
106
107 System.err.println("List results differ (actual vs. expected):");
108
109 for (int i = 0; i < count; i++)
110 {
111 System.err.printf(format, i, get(actualStrings, i), get(expectedStrings, i));
112 }
113 }
114
115 private static String get(List<String> list, int index)
116 {
117 if (index < list.size())
118 return list.get(index);
119
120 return "";
121 }
122
123 private static int maxLength(List<String> list)
124 {
125 int result = 0;
126
127 for (String s : list)
128 {
129 result = Math.max(result, s.length());
130 }
131
132 return result;
133 }
134
135 private static <T> List<String> toStrings(List<T> list)
136 {
137 List<String> result = new ArrayList<String>();
138
139 for (T t : list)
140 {
141 result.add(String.valueOf(t));
142 }
143
144 return result;
145 }
146
147 /**
148 * Convenience for {@link #assertListsEquals(List, List)}.
149 *
150 * @param <T>
151 * type of objects to compare
152 * @param actual
153 * actual values to check
154 * @param expected
155 * expected values
156 */
157 public static <T> void assertListsEquals(List<T> actual, T... expected)
158 {
159 assertListsEquals(actual, Arrays.asList(expected));
160 }
161
162 /**
163 * Convenience for {@link #assertListsEquals(List, List)}.
164 *
165 * @param <T>
166 * type of objects to compare
167 * @param actual
168 * actual values to check
169 * @param expected
170 * expected values
171 */
172 public static <T> void assertArraysEqual(T[] actual, T... expected)
173 {
174 assertListsEquals(Arrays.asList(actual), expected);
175 }
176
177 /**
178 * Initializes private fields (via reflection).
179 *
180 * @param object
181 * object to be updated
182 * @param fieldValues
183 * string field names and corresponding field values
184 * @return the object
185 */
186 public static <T> T set(T object, Object... fieldValues)
187 {
188 assert object != null;
189 Class objectClass = object.getClass();
190
191 for (int i = 0; i < fieldValues.length; i += 2)
192 {
193 String fieldName = (String) fieldValues[i];
194 Object fieldValue = fieldValues[i + 1];
195
196 try
197 {
198 Field field = findField(objectClass, fieldName);
199
200 field.setAccessible(true);
201
202 field.set(object, fieldValue);
203 }
204 catch (Exception ex)
205 {
206 throw new RuntimeException(String.format("Unable to set field '%s' of %s to %s: %s", fieldName, object,
207 fieldValue, toMessage(ex)), ex);
208 }
209 }
210
211 return object;
212 }
213
214 /**
215 * Reads the content of a private field.
216 *
217 * @param object
218 * to read the private field from
219 * @param fieldName
220 * name of field to read
221 * @return value stored in the field
222 * @since 5.1.0.5
223 */
224 public static Object get(Object object, String fieldName)
225 {
226 assert object != null;
227
228 try
229 {
230 Field field = findField(object.getClass(), fieldName);
231
232 field.setAccessible(true);
233
234 return field.get(object);
235 }
236 catch (Exception ex)
237 {
238 throw new RuntimeException(String.format("Unable to read field '%s' of %s: %s", fieldName, object,
239 toMessage(ex)), ex);
240 }
241 }
242
243 private static String toMessage(Throwable exception)
244 {
245 String message = exception.getMessage();
246
247 if (message != null)
248 return message;
249
250 return exception.getClass().getName();
251 }
252
253 private static Field findField(Class objectClass, String fieldName)
254 {
255
256 Class cursor = objectClass;
257
258 while (cursor != null)
259 {
260 try
261 {
262 return cursor.getDeclaredField(fieldName);
263 }
264 catch (NoSuchFieldException ex)
265 {
266 // Ignore.
267 }
268
269 cursor = cursor.getSuperclass();
270 }
271
272 throw new RuntimeException(String.format("Class %s does not contain a field named '%s'.",
273 objectClass.getName(), fieldName));
274 }
275
276 /**
277 * Creates a new instance of the object using its default constructor, and initializes it (via
278 * {@link #set(Object, Object[])}).
279 *
280 * @param objectType
281 * typeof object to instantiate
282 * @param fieldValues
283 * string field names and corresponding field values
284 * @return the initialized instance
285 */
286 public static <T> T create(Class<T> objectType, Object... fieldValues)
287 {
288 T result = null;
289
290 try
291 {
292 result = objectType.newInstance();
293 }
294 catch (Exception ex)
295 {
296 throw new RuntimeException(String.format("Unable to instantiate instance of %s: %s", objectType.getName(),
297 toMessage(ex)), ex);
298 }
299
300 return set(result, fieldValues);
301 }
302
303 }