/* * Copyright 2016 the original author or authors. * * 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.powermock.api.extension.listener; import org.easymock.Mock; import org.easymock.TestSubject; import org.powermock.reflect.Whitebox; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; /** * The class injects mocks created with {@link Mock}, {@link org.powermock.api.easymock.annotation.Mock} * and {@link org.powermock.core.classloader.annotations.Mock} to fields of objects which is annotated with {@link TestSubject} * @see TestSubject * @since 1.6.5 */ @SuppressWarnings({"deprecation", "WeakerAccess"}) class TestSubjectInjector { private final Object testInstance; private final AnnotationGlobalMetadata globalMetadata; public TestSubjectInjector(Object testInstance, AnnotationGlobalMetadata globalMetadata) { this.testInstance = testInstance; this.globalMetadata = globalMetadata; } @SuppressWarnings("unchecked") protected void injectTestSubjectMocks() throws IllegalAccessException { Set<Field> testSubjectFields = Whitebox.getFieldsAnnotatedWith(testInstance, TestSubject.class); for (Field testSubjectField : testSubjectFields) { Object testSubject = testSubjectField.get(testInstance); if (testSubject == null) { throw new NullPointerException("Have you forgotten to instantiate " + testSubjectField.getName() + "?"); } injectTestSubjectFields(testSubject); } } protected void injectTestSubjectFields(Object testSubject) throws IllegalAccessException { Set<Field> targetFields = new HashSet<Field>(Whitebox.getAllInstanceFields(testSubject)); targetFields = injectByName(targetFields, testSubject); injectByType(targetFields, testSubject); } void injectByType(Set<Field> targetFields, Object testSubject) throws IllegalAccessException { for (Field targetField : targetFields) { InjectionTarget target = new InjectionTarget(targetField); MockMetadata toAssign = findUniqueAssignable(target); if (toAssign == null) { continue; } target.inject(testSubject, toAssign); } } MockMetadata findUniqueAssignable(InjectionTarget target) { MockMetadata toAssign = null; for (MockMetadata mockMetadata : globalMetadata.getUnqualifiedInjections()) { if (target.accepts(mockMetadata)) { if (toAssign != null) { throw new RuntimeException(String.format("At least two mocks can be assigned to '%s': %s and %s", target.getField(), toAssign.getMock(), mockMetadata.getMock())); } toAssign = mockMetadata; } } return toAssign; } Set<Field> injectByName(Set<Field> targetFields, Object targetObject) throws IllegalAccessException { Class<?> targetClass = targetObject.getClass(); for (MockMetadata mockMetadata : globalMetadata.getQualifiedInjections()) { Field targetField = getFieldByName(targetClass, mockMetadata.getQualifier()); if (targetField == null) { continue; } InjectionTarget target = new InjectionTarget(targetField); if (target.accepts(mockMetadata)) { target.inject(targetObject, mockMetadata); targetFields.remove(targetField); } } return targetFields; } private Field getFieldByName(Class<?> clazz, String fieldName) { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { return null; } catch (SecurityException e) { return null; } } private static class InjectionTarget { private final Field field; public InjectionTarget(Field field) { this.field = field; } public Field getField() { return field; } public boolean accepts(MockMetadata mockMetadata) { return field.getType().isAssignableFrom(mockMetadata.getType()); } public void inject(Object targetObject, MockMetadata mockMetadata) throws IllegalAccessException { field.setAccessible(true); Object value = field.get(targetObject); if (value == null) { field.set(targetObject, mockMetadata.getMock()); } } } }