// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.ioc.test;
import org.testng.Assert;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Extra assertions on top of the standard set, packaged as a base class for easy referencing in tests. Also,
* utilities for instantiation objects and setting and reading private fields of those objects.
*
* This class was originally in the tapestry-ioc module as was moved to tapestry-test; the package name was not changed
* to ensure backwards compatibility.
*
* @since 5.2.0
* @deprecated In 5.4, with no replacement
*/
public class TestUtils extends Assert
{
/**
* Invoked from code that should not be reachable. For example, place a call to unreachable() after invoking a
* method that is expected to throw an exception.
*/
public static void unreachable()
{
fail("This code should not be reachable.");
}
/**
* Asserts that the message property of the throwable contains each of the provided substrings.
*
* @param t
* throwable to check
* @param substrings
* some number of expected substrings
*/
public static void assertMessageContains(Throwable t, String... substrings)
{
String message = t.getMessage();
for (String substring : substrings)
assertTrue(message.contains(substring), String.format("String '%s' not found in '%s'.", substring, message));
}
/**
* Compares two lists for equality; first all the elements are individually compared for equality (if the lists are
* of unequal length, only elements up to the shorter length are compared). Then the length of the lists are
* compared. This generally gives
*
* @param <T>
* type of objects to compare
* @param actual
* actual values to check
* @param expected
* expected values
*/
public static <T> void assertListsEquals(List<T> actual, List<T> expected)
{
int count = Math.min(actual.size(), expected.size());
try
{
for (int i = 0; i < count; i++)
{
assertEquals(actual.get(i), expected.get(i), String.format("Element #%d.", i));
}
assertEquals(actual.size(), expected.size(), "List size.");
}
catch (AssertionError ae)
{
showLists(actual, expected);
throw ae;
}
}
protected static <T> void showLists(List<T> actual, List<T> expected)
{
List<String> actualStrings = toStrings(actual);
List<String> expectedStrings = toStrings(expected);
String format = String
.format("%%3d: [%%-%ds] [%%-%ds]\n", maxLength(actualStrings), maxLength(expectedStrings));
int count = Math.max(actual.size(), expected.size());
System.out.flush();
System.err.flush();
System.err.println("List results differ (actual vs. expected):");
for (int i = 0; i < count; i++)
{
System.err.printf(format, i, get(actualStrings, i), get(expectedStrings, i));
}
}
private static String get(List<String> list, int index)
{
if (index < list.size())
return list.get(index);
return "";
}
private static int maxLength(List<String> list)
{
int result = 0;
for (String s : list)
{
result = Math.max(result, s.length());
}
return result;
}
private static <T> List<String> toStrings(List<T> list)
{
List<String> result = new ArrayList<String>();
for (T t : list)
{
result.add(String.valueOf(t));
}
return result;
}
/**
* Convenience for {@link #assertListsEquals(List, List)}.
*
* @param <T>
* type of objects to compare
* @param actual
* actual values to check
* @param expected
* expected values
*/
public static <T> void assertListsEquals(List<T> actual, T... expected)
{
assertListsEquals(actual, Arrays.asList(expected));
}
/**
* Convenience for {@link #assertListsEquals(List, List)}.
*
* @param <T>
* type of objects to compare
* @param actual
* actual values to check
* @param expected
* expected values
*/
public static <T> void assertArraysEqual(T[] actual, T... expected)
{
assertListsEquals(Arrays.asList(actual), expected);
}
/**
* Initializes private fields (via reflection).
*
* @param object
* object to be updated
* @param fieldValues
* string field names and corresponding field values
* @return the object
*/
public static <T> T set(T object, Object... fieldValues)
{
assert object != null;
Class objectClass = object.getClass();
for (int i = 0; i < fieldValues.length; i += 2)
{
String fieldName = (String) fieldValues[i];
Object fieldValue = fieldValues[i + 1];
try
{
Field field = findField(objectClass, fieldName);
field.setAccessible(true);
field.set(object, fieldValue);
}
catch (Exception ex)
{
throw new RuntimeException(String.format("Unable to set field '%s' of %s to %s: %s", fieldName, object,
fieldValue, toMessage(ex)), ex);
}
}
return object;
}
/**
* Reads the content of a private field.
*
* @param object
* to read the private field from
* @param fieldName
* name of field to read
* @return value stored in the field
* @since 5.1.0.5
*/
public static Object get(Object object, String fieldName)
{
assert object != null;
try
{
Field field = findField(object.getClass(), fieldName);
field.setAccessible(true);
return field.get(object);
}
catch (Exception ex)
{
throw new RuntimeException(String.format("Unable to read field '%s' of %s: %s", fieldName, object,
toMessage(ex)), ex);
}
}
private static String toMessage(Throwable exception)
{
String message = exception.getMessage();
if (message != null)
return message;
return exception.getClass().getName();
}
private static Field findField(Class objectClass, String fieldName)
{
Class cursor = objectClass;
while (cursor != null)
{
try
{
return cursor.getDeclaredField(fieldName);
}
catch (NoSuchFieldException ex)
{
// Ignore.
}
cursor = cursor.getSuperclass();
}
throw new RuntimeException(String.format("Class %s does not contain a field named '%s'.",
objectClass.getName(), fieldName));
}
/**
* Creates a new instance of the object using its default constructor, and initializes it (via
* {@link #set(Object, Object[])}).
*
* @param objectType
* typeof object to instantiate
* @param fieldValues
* string field names and corresponding field values
* @return the initialized instance
*/
public static <T> T create(Class<T> objectType, Object... fieldValues)
{
T result = null;
try
{
result = objectType.newInstance();
}
catch (Exception ex)
{
throw new RuntimeException(String.format("Unable to instantiate instance of %s: %s", objectType.getName(),
toMessage(ex)), ex);
}
return set(result, fieldValues);
}
}