/* * Copyright (c) 2006-2013 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.expectations.mocking; import java.lang.reflect.*; import java.util.*; import mockit.external.asm4.*; import mockit.internal.*; import mockit.internal.state.*; import mockit.internal.util.*; public final class DynamicPartialMocking { public final List<Object> targetInstances; private final Map<Class<?>, byte[]> modifiedClassfiles; private final boolean nonStrict; public DynamicPartialMocking(boolean nonStrict) { targetInstances = new ArrayList<Object>(2); modifiedClassfiles = new HashMap<Class<?>, byte[]>(); this.nonStrict = nonStrict; } public void redefineTypes(Object[] classesOrInstancesToBePartiallyMocked) { for (Object classOrInstance : classesOrInstancesToBePartiallyMocked) { redefineTargetType(classOrInstance); } new RedefinitionEngine().redefineMethods(modifiedClassfiles); modifiedClassfiles.clear(); } private void redefineTargetType(Object classOrInstance) { Class<?> targetClass; if (classOrInstance instanceof Class) { targetClass = (Class<?>) classOrInstance; validateTargetClassType(targetClass); registerAsMocked(targetClass); redefineClassAndItsSuperClasses(targetClass, false); } else { targetClass = GeneratedClasses.getMockedClass(classOrInstance); validateTargetClassType(targetClass); registerAsMocked(classOrInstance); redefineClassAndItsSuperClasses(targetClass, true); targetInstances.add(classOrInstance); } TestRun.mockFixture().registerMockedClass(targetClass); } private void validateTargetClassType(Class<?> targetClass) { if ( targetClass.isInterface() || targetClass.isAnnotation() || targetClass.isArray() || targetClass.isPrimitive() || AutoBoxing.isWrapperOfPrimitiveType(targetClass) || GeneratedClasses.isGeneratedImplementationClass(targetClass) ) { throw new IllegalArgumentException("Invalid type for dynamic mocking: " + targetClass); } } public void registerAsMocked(Class<?> mockedClass) { if (nonStrict) { ExecutingTest executingTest = TestRun.getExecutingTest(); do { executingTest.registerAsNonStrictlyMocked(mockedClass); //noinspection AssignmentToMethodParameter mockedClass = mockedClass.getSuperclass(); } while (mockedClass != null && mockedClass != Object.class && mockedClass != Proxy.class); } } private void registerAsMocked(Object mock) { if (nonStrict) { TestRun.getExecutingTest().registerAsNonStrictlyMocked(mock); } } private void redefineClassAndItsSuperClasses(Class<?> realClass, boolean methodsOnly) { redefineClass(realClass, methodsOnly); Class<?> superClass = realClass.getSuperclass(); if (superClass != null && superClass != Object.class && superClass != Proxy.class) { redefineClassAndItsSuperClasses(superClass, methodsOnly); } } private void redefineClass(Class<?> realClass, boolean methodsOnly) { ClassReader classReader = ClassFile.createReaderOrGetFromCache(realClass); ExpectationsModifier modifier = new ExpectationsModifier(realClass.getClassLoader(), classReader, null); modifier.useDynamicMocking(methodsOnly); classReader.accept(modifier, 0); byte[] modifiedClass = modifier.toByteArray(); modifiedClassfiles.put(realClass, modifiedClass); } }