All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
mockit.internal.expectations.injection.TestedClassInstantiations Maven / Gradle / Ivy
Go to download
JMockit is a Java toolkit for developer (unit/integration) testing.
It contains mocking APIs and other tools, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006-2012 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.injection;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.lang.reflect.Type;
import java.util.*;
import javax.inject.*;
import static java.lang.reflect.Modifier.*;
import mockit.*;
import mockit.external.asm4.*;
import mockit.internal.*;
import mockit.internal.expectations.mocking.*;
import mockit.internal.state.*;
import mockit.internal.util.*;
import static mockit.internal.util.Utilities.*;
public final class TestedClassInstantiations
{
private static final Class extends Annotation> INJECT_CLASS;
static
{
Class extends Annotation> injectClass;
ClassLoader cl = TestedClassInstantiations.class.getClassLoader();
try {
//noinspection unchecked
injectClass = (Class extends Annotation>) Class.forName("javax.inject.Inject", false, cl);
}
catch (ClassNotFoundException ignore) { injectClass = null; }
INJECT_CLASS = injectClass;
}
private final List testedFields;
private final List injectableFields;
private List injectables;
private final List consumedInjectables;
private Object testClassInstance;
private Type typeOfInjectionPoint;
private final class TestedField
{
final Field testedField;
private TestedObjectCreation testedObjectCreation;
private List targetFields;
TestedField(Field field) { testedField = field; }
void instantiateWithInjectableValues()
{
Object testedObject = FieldReflection.getFieldValue(testedField, testClassInstance);
boolean requiresJavaxInject = false;
Class> testedClass;
if (testedObject == null && !isFinal(testedField.getModifiers())) {
if (testedObjectCreation == null) {
testedObjectCreation = new TestedObjectCreation(testedField);
}
testedClass = testedObjectCreation.declaredClass;
testedObject = testedObjectCreation.create();
FieldReflection.setFieldValue(testedField, testClassInstance, testedObject);
requiresJavaxInject = testedObjectCreation.constructorAnnotatedWithJavaxInject;
}
else {
testedClass = testedObject == null ? null : testedObject.getClass();
}
if (testedObject != null) {
FieldInjection fieldInjection = new FieldInjection(testedClass, testedObject, requiresJavaxInject);
if (targetFields == null) {
targetFields = fieldInjection.findAllTargetInstanceFieldsInTestedClassHierarchy();
}
fieldInjection.injectIntoEligibleFields(targetFields);
}
}
}
public TestedClassInstantiations()
{
testedFields = new LinkedList();
injectableFields = new ArrayList();
consumedInjectables = new ArrayList();
}
public boolean findTestedAndInjectableFields(Class> testClass)
{
new ParameterNameExtractor(true).extractNames(testClass);
Field[] fieldsInTestClass = testClass.getDeclaredFields();
for (Field field : fieldsInTestClass) {
if (field.isAnnotationPresent(Tested.class)) {
testedFields.add(new TestedField(field));
}
else {
MockedType mockedType = new MockedType(field, true);
if (mockedType.injectable) {
injectableFields.add(mockedType);
}
}
}
return !testedFields.isEmpty();
}
public void assignNewInstancesToTestedFields(Object testClassInstance)
{
this.testClassInstance = testClassInstance;
buildListsOfInjectables();
for (TestedField testedField : testedFields) {
testedField.instantiateWithInjectableValues();
consumedInjectables.clear();
}
}
private void buildListsOfInjectables()
{
ParameterTypeRedefinitions paramTypeRedefs = TestRun.getExecutingTest().getParameterTypeRedefinitions();
if (paramTypeRedefs == null) {
injectables = injectableFields;
}
else {
injectables = new ArrayList(injectableFields);
injectables.addAll(paramTypeRedefs.getInjectableParameters());
}
}
void setTypeOfInjectionPoint(Type parameterOrFieldType) { typeOfInjectionPoint = parameterOrFieldType; }
boolean hasSameTypeAsInjectionPoint(MockedType injectable)
{
return isSameTypeAsInjectionPoint(injectable.declaredType);
}
boolean isSameTypeAsInjectionPoint(Type injectableType)
{
if (typeOfInjectionPoint.equals(injectableType)) return true;
if (INJECT_CLASS != null && typeOfInjectionPoint instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) typeOfInjectionPoint;
if (parameterizedType.getRawType() == Provider.class) {
Type providedType = parameterizedType.getActualTypeArguments()[0];
return providedType.equals(injectableType);
}
}
return false;
}
private Object getValueToInject(MockedType injectable)
{
if (consumedInjectables.contains(injectable)) {
return null;
}
Object value = injectable.getValueToInject(testClassInstance);
if (value != null) {
consumedInjectables.add(injectable);
}
return value;
}
private Object wrapInProviderIfNeeded(Type type, final Object value)
{
if (
INJECT_CLASS != null && type instanceof ParameterizedType && !(value instanceof Provider) &&
((ParameterizedType) type).getRawType() == Provider.class
) {
return new Provider() { public Object get() { return value; } };
}
return value;
}
private final class TestedObjectCreation
{
private final Class> declaredClass;
private final Class> actualClass;
private Constructor> constructor;
private List injectablesForConstructor;
private Type[] parameterTypes;
boolean constructorAnnotatedWithJavaxInject;
TestedObjectCreation(Field testedField)
{
declaredClass = testedField.getType();
actualClass =
isAbstract(declaredClass.getModifiers()) ? generateSubclass(testedField.getGenericType()) : declaredClass;
}
private Class> generateSubclass(Type testedType)
{
ClassReader classReader = ClassFile.createReaderOrGetFromCache(declaredClass);
String subclassName = GeneratedClasses.getNameForGeneratedClass(declaredClass);
ClassVisitor modifier = new SubclassGenerationModifier(testedType, classReader, subclassName);
classReader.accept(modifier, 0);
return new ImplementationClass().defineNewClass(declaredClass.getClassLoader(), modifier, subclassName);
}
Object create()
{
new ConstructorSearch().findConstructorAccordingToAccessibilityAndAvailableInjectables();
if (constructor == null) {
throw new IllegalArgumentException(
"No constructor in " + declaredClass + " that can be satisfied by available injectables");
}
return new ConstructorInjection().instantiate();
}
MockedType findNextInjectableForVarargsParameter()
{
for (MockedType injectable : injectables) {
if (hasSameTypeAsInjectionPoint(injectable) && !consumedInjectables.contains(injectable)) {
return injectable;
}
}
return null;
}
private final class ConstructorSearch
{
private final String testedClassDesc;
ConstructorSearch()
{
testedClassDesc = new ParameterNameExtractor(false).extractNames(declaredClass);
injectablesForConstructor = new ArrayList();
}
void findConstructorAccordingToAccessibilityAndAvailableInjectables()
{
constructor = null;
Constructor>[] constructors = actualClass.getDeclaredConstructors();
if (INJECT_CLASS != null && findSingleInjectAnnotatedConstructor(constructors)) {
constructorAnnotatedWithJavaxInject = true;
}
else {
findSatisfiedConstructorWithMostParameters(constructors);
}
}
private boolean findSingleInjectAnnotatedConstructor(Constructor>[] constructors)
{
for (Constructor> c : constructors) {
if (c.isAnnotationPresent(INJECT_CLASS)) {
List injectablesFound = findAvailableInjectablesForConstructor(c);
if (injectablesFound != null) {
injectablesForConstructor = injectablesFound;
constructor = c;
}
return true;
}
}
return false;
}
private void findSatisfiedConstructorWithMostParameters(Constructor>[] constructors)
{
Arrays.sort(constructors, new Comparator>() {
public int compare(Constructor> c1, Constructor> c2)
{
int m1 = constructorModifiers(c1);
int m2 = constructorModifiers(c2);
if (m1 == m2) return 0;
if (m1 == PUBLIC) return -1;
if (m2 == PUBLIC) return 1;
if (m1 == PROTECTED) return -1;
if (m2 == PROTECTED) return 1;
if (m2 == PRIVATE) return -1;
return 1;
}
});
for (Constructor> c : constructors) {
List injectablesFound = findAvailableInjectablesForConstructor(c);
if (
injectablesFound != null &&
(constructor == null ||
constructorModifiers(c) == constructorModifiers(constructor) &&
injectablesFound.size() >= injectablesForConstructor.size())
) {
injectablesForConstructor = injectablesFound;
constructor = c;
}
}
}
private static final int CONSTRUCTOR_ACCESS = PUBLIC + PROTECTED + PRIVATE;
private int constructorModifiers(Constructor> c) { return CONSTRUCTOR_ACCESS & c.getModifiers(); }
private List findAvailableInjectablesForConstructor(Constructor> candidate)
{
parameterTypes = candidate.getGenericParameterTypes();
int n = parameterTypes.length;
List injectablesFound = new ArrayList(n);
boolean varArgs = candidate.isVarArgs();
if (varArgs) {
n--;
}
String constructorDesc = "" + mockit.external.asm4.Type.getConstructorDescriptor(candidate);
for (int i = 0; i < n; i++) {
setTypeOfInjectionPoint(parameterTypes[i]);
String parameterName = ParameterNames.getName(testedClassDesc, constructorDesc, i);
MockedType injectable = parameterName == null ? null : findInjectable(parameterName);
if (injectable == null || injectablesFound.contains(injectable)) {
return null;
}
injectablesFound.add(injectable);
}
if (varArgs) {
MockedType injectable = hasInjectedValuesForVarargsParameter(n);
if (injectable != null) {
injectablesFound.add(injectable);
}
}
return injectablesFound;
}
private MockedType findInjectable(String nameOfInjectionPoint)
{
boolean multipleInjectablesFound = false;
MockedType found = null;
for (MockedType injectable : injectables) {
if (hasSameTypeAsInjectionPoint(injectable)) {
if (found == null) {
found = injectable;
}
else {
if (nameOfInjectionPoint.equals(injectable.mockId)) {
return injectable;
}
multipleInjectablesFound = true;
}
}
}
if (multipleInjectablesFound && !nameOfInjectionPoint.equals(found.mockId)) {
return null;
}
return found;
}
private MockedType hasInjectedValuesForVarargsParameter(int varargsParameterIndex)
{
getTypeOfInjectionPointFromVarargsParameter(varargsParameterIndex);
return findNextInjectableForVarargsParameter();
}
}
private Type getTypeOfInjectionPointFromVarargsParameter(int varargsParameterIndex)
{
Type parameterType = parameterTypes[varargsParameterIndex];
if (parameterType instanceof Class>) {
parameterType = ((Class>) parameterType).getComponentType();
}
else {
parameterType = ((GenericArrayType) parameterType).getGenericComponentType();
}
setTypeOfInjectionPoint(parameterType);
return parameterType;
}
private final class ConstructorInjection
{
Object instantiate()
{
parameterTypes = constructor.getGenericParameterTypes();
int n = parameterTypes.length;
Object[] arguments = new Object[n];
boolean varArgs = constructor.isVarArgs();
if (varArgs) {
n--;
}
for (int i = 0; i < n; i++) {
MockedType injectable = injectablesForConstructor.get(i);
Object value = getArgumentValueToInject(injectable);
arguments[i] = wrapInProviderIfNeeded(parameterTypes[i], value);
}
if (varArgs) {
arguments[n] = obtainInjectedVarargsArray(n);
}
return ConstructorReflection.invoke(constructor, arguments);
}
private Object obtainInjectedVarargsArray(int varargsParameterIndex)
{
Type varargsElementType = getTypeOfInjectionPointFromVarargsParameter(varargsParameterIndex);
List varargValues = new ArrayList();
MockedType injectable;
while ((injectable = findNextInjectableForVarargsParameter()) != null) {
Object value = getValueToInject(injectable);
if (value != null) {
value = wrapInProviderIfNeeded(varargsElementType, value);
varargValues.add(value);
}
}
int elementCount = varargValues.size();
Object varargArray = Array.newInstance(getClassType(varargsElementType), elementCount);
for (int i = 0; i < elementCount; i++) {
Array.set(varargArray, i, varargValues.get(i));
}
return varargArray;
}
private Object getArgumentValueToInject(MockedType injectable)
{
Object argument = getValueToInject(injectable);
if (argument == null) {
throw new IllegalArgumentException(
"No injectable value available" + missingInjectableDescription(injectable.mockId));
}
return argument;
}
private String missingInjectableDescription(String name)
{
String classDesc = mockit.external.asm4.Type.getInternalName(constructor.getDeclaringClass());
String constructorDesc = "" + mockit.external.asm4.Type.getConstructorDescriptor(constructor);
String constructorDescription = new MethodFormatter(classDesc, constructorDesc).toString();
return
" for parameter \"" + name + "\" in constructor " +
constructorDescription.replace("java.lang.", "");
}
}
}
private final class FieldInjection
{
private final Class> testedClass;
private final Object testedObject;
private final boolean requiresJavaxInject;
private boolean foundJavaxInject;
private FieldInjection(Class> testedClass, Object testedObject, boolean requiresJavaxInject)
{
this.testedClass = testedClass;
this.testedObject = testedObject;
this.requiresJavaxInject = requiresJavaxInject;
}
List findAllTargetInstanceFieldsInTestedClassHierarchy()
{
List targetFields = new ArrayList();
Class> classWithFields = testedClass;
do {
Field[] fields = classWithFields.getDeclaredFields();
for (Field field : fields) {
if (isEligibleForInjection(field)) {
targetFields.add(field);
}
}
classWithFields = classWithFields.getSuperclass();
}
while (isFromSameModuleOrSystemAsSuperClass(classWithFields));
discardFieldsNotAnnotatedWithJavaxInjectIfAtLeastOneIsAnnotated(targetFields);
return targetFields;
}
private boolean isEligibleForInjection(Field field)
{
if (isFinal(field.getModifiers())) return false;
if (requiresJavaxInject) return isAnnotatedWithJavaxInject(field);
boolean notStatic = !isStatic(field.getModifiers());
return INJECT_CLASS == null ? notStatic : isAnnotatedWithJavaxInject(field) || notStatic;
}
private boolean isAnnotatedWithJavaxInject(Field field)
{
boolean annotated = field.isAnnotationPresent(INJECT_CLASS);
if (annotated) foundJavaxInject = true;
return annotated;
}
private void discardFieldsNotAnnotatedWithJavaxInjectIfAtLeastOneIsAnnotated(List targetFields)
{
if (!requiresJavaxInject && foundJavaxInject) {
ListIterator itr = targetFields.listIterator();
while (itr.hasNext()) {
Field targetField = itr.next();
if (!targetField.isAnnotationPresent(INJECT_CLASS)) {
itr.remove();
}
}
}
}
private boolean isFromSameModuleOrSystemAsSuperClass(Class> superClass)
{
if (superClass.getClassLoader() == null) {
return false;
}
if (superClass.getProtectionDomain() == testedClass.getProtectionDomain()) {
return true;
}
String className1 = superClass.getName();
String className2 = testedClass.getName();
int p1 = className1.indexOf('.');
int p2 = className2.indexOf('.');
if (p1 != p2 || p1 == -1) {
return false;
}
p1 = className1.indexOf('.', p1 + 1);
p2 = className2.indexOf('.', p2 + 1);
return p1 == p2 && p1 > 0 && className1.substring(0, p1).equals(className2.substring(0, p2));
}
void injectIntoEligibleFields(List targetFields)
{
for (Field field : targetFields) {
if (notAssignedByConstructor(field)) {
Object injectableValue = getValueForFieldIfAvailable(targetFields, field);
if (injectableValue != null) {
injectableValue = wrapInProviderIfNeeded(field.getGenericType(), injectableValue);
FieldReflection.setFieldValue(field, testedObject, injectableValue);
}
}
}
}
private boolean notAssignedByConstructor(Field field)
{
if (INJECT_CLASS != null && field.isAnnotationPresent(INJECT_CLASS)) {
return true;
}
Object fieldValue = FieldReflection.getFieldValue(field, testedObject);
if (fieldValue == null) {
return true;
}
Class> fieldType = field.getType();
if (!fieldType.isPrimitive()) {
return false;
}
Object defaultValue = DefaultValues.defaultValueForPrimitiveType(fieldType);
return fieldValue.equals(defaultValue);
}
private Object getValueForFieldIfAvailable(List targetFields, Field fieldToBeInjected)
{
setTypeOfInjectionPoint(fieldToBeInjected.getGenericType());
String targetFieldName = fieldToBeInjected.getName();
MockedType mockedType;
if (withMultipleTargetFieldsOfSameType(targetFields, fieldToBeInjected)) {
mockedType = findInjectableByTypeAndName(targetFieldName);
}
else {
mockedType = findInjectableByTypeAndOptionallyName(targetFieldName);
}
return mockedType == null ? null : getValueToInject(mockedType);
}
private boolean withMultipleTargetFieldsOfSameType(List targetFields, Field fieldToBeInjected)
{
for (Field targetField : targetFields) {
if (targetField != fieldToBeInjected && isSameTypeAsInjectionPoint(targetField.getGenericType())) {
return true;
}
}
return false;
}
private MockedType findInjectableByTypeAndName(String targetFieldName)
{
for (MockedType injectable : injectables) {
if (hasSameTypeAsInjectionPoint(injectable) && targetFieldName.equals(injectable.mockId)) {
return injectable;
}
}
return null;
}
private MockedType findInjectableByTypeAndOptionallyName(String targetFieldName)
{
MockedType found = null;
for (MockedType injectable : injectables) {
if (hasSameTypeAsInjectionPoint(injectable)) {
if (targetFieldName.equals(injectable.mockId)) {
return injectable;
}
if (found == null) {
found = injectable;
}
}
}
return found;
}
}
}