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 }