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

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

/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.expectations.mocking;

import java.lang.instrument.*;
import java.util.*;
import javax.annotation.*;

import mockit.asm.classes.*;
import mockit.asm.types.*;
import mockit.internal.*;
import mockit.internal.capturing.*;
import mockit.internal.startup.*;
import mockit.internal.state.*;

public final class CaptureOfNewInstances extends CaptureOfImplementations
{
   @Nonnull private final List> baseTypes;
   @Nonnull private final List> partiallyMockedBaseTypes;

   CaptureOfNewInstances() {
      baseTypes = new ArrayList<>();
      partiallyMockedBaseTypes = new ArrayList<>();
   }

   void useDynamicMocking(@Nonnull Class baseType) {
      partiallyMockedBaseTypes.add(baseType);

      List> mockedClasses = TestRun.mockFixture().getMockedClasses();

      for (Class mockedClass : mockedClasses) {
         if (baseType.isAssignableFrom(mockedClass)) {
            if (mockedClass != baseType || !baseType.isInterface()) {
               redefineClassForDynamicPartialMocking(baseType, mockedClass);
            }
         }
      }
   }

   private static void redefineClassForDynamicPartialMocking(@Nonnull Class baseType, @Nonnull Class mockedClass) {
      ClassReader classReader = ClassFile.createReaderOrGetFromCache(mockedClass);

      MockedClassModifier modifier = newModifier(mockedClass.getClassLoader(), classReader, baseType, null);
      modifier.useDynamicMocking(true);
      classReader.accept(modifier);
      byte[] modifiedClassfile = modifier.toByteArray();

      Startup.redefineMethods(mockedClass, modifiedClassfile);
   }

   @Nonnull
   private static MockedClassModifier newModifier(
      @Nullable ClassLoader cl, @Nonnull ClassReader cr, @Nonnull Class baseType, @Nullable MockedType typeMetadata
   ) {
      MockedClassModifier modifier = new MockedClassModifier(cl, cr, typeMetadata);
      String baseTypeDesc = JavaType.getInternalName(baseType);
      modifier.setClassNameForCapturedInstanceMethods(baseTypeDesc);
      return modifier;
   }

   @Nonnull @Override
   protected BaseClassModifier createModifier(
      @Nullable ClassLoader cl, @Nonnull ClassReader cr, @Nonnull Class baseType, @Nullable MockedType typeMetadata
   ) {
      MockedClassModifier modifier = newModifier(cl, cr, baseType, typeMetadata);

      if (partiallyMockedBaseTypes.contains(baseType)) {
         modifier.useDynamicMocking(true);
      }

      return modifier;
   }

   @Override
   protected void redefineClass(@Nonnull Class realClass, @Nonnull byte[] modifiedClass) {
      ClassDefinition newClassDefinition = new ClassDefinition(realClass, modifiedClass);
      Startup.redefineMethods(newClassDefinition);

      MockFixture mockFixture = TestRun.mockFixture();
      mockFixture.addRedefinedClass(newClassDefinition);
      mockFixture.registerMockedClass(realClass);
   }

   void registerCaptureOfNewInstances(@Nonnull MockedType typeMetadata) {
      Class baseType = typeMetadata.getClassType();

      if (!typeMetadata.isFinalFieldOrParameter()) {
         makeSureAllSubtypesAreModified(typeMetadata);
      }

      if (!baseTypes.contains(baseType)) {
         baseTypes.add(baseType);
      }
   }

   void makeSureAllSubtypesAreModified(@Nonnull MockedType typeMetadata) {
      Class baseType = typeMetadata.getClassType();
      makeSureAllSubtypesAreModified(baseType, typeMetadata.fieldFromTestClass, typeMetadata);
   }

   public boolean captureNewInstance(@Nonnull Object mock) {
      Class mockedClass = mock.getClass();
      return !baseTypes.contains(mockedClass) && isWithCapturing(mockedClass);
   }

   private boolean isWithCapturing(@Nonnull Class mockedClass) {
      Class[] interfaces = mockedClass.getInterfaces();

      for (Class anInterface : interfaces) {
         if (baseTypes.contains(anInterface)) {
            return true;
         }
      }

      Class superclass = mockedClass.getSuperclass();
      return superclass != Object.class && (baseTypes.contains(superclass) || isWithCapturing(superclass));
   }

   void cleanUp() {
      baseTypes.clear();
      partiallyMockedBaseTypes.clear();
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy