All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.expectations.mocking.DynamicPartialMocking Maven / Gradle / Ivy

/*
 * 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 targetInstances;
   private final Map, byte[]> modifiedClassfiles;
   private final boolean nonStrict;

   public DynamicPartialMocking(boolean nonStrict)
   {
      targetInstances = new ArrayList(2);
      modifiedClassfiles = new HashMap, 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);
   }
}