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

mockit.internal.expectations.mocking.CaptureOfNewInstances 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.

The newest version!
/*
 * 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.util.*;

import org.jetbrains.annotations.*;

import mockit.external.asm4.*;
import mockit.internal.capturing.*;
import mockit.internal.state.*;
import mockit.internal.util.*;

import static mockit.internal.util.FieldReflection.*;

public class CaptureOfNewInstances extends CaptureOfImplementations
{
   static final class Capture
   {
      @NotNull final MockedType typeMetadata;
      @Nullable private Object originalMockInstance;
      @NotNull private final List instancesCaptured;

      private Capture(@NotNull MockedType typeMetadata, @Nullable Object originalMockInstance)
      {
         this.typeMetadata = typeMetadata;
         this.originalMockInstance = originalMockInstance;
         instancesCaptured = new ArrayList(4);
      }

      private boolean isInstanceAlreadyCaptured(@NotNull Object mock)
      {
         return Utilities.containsReference(instancesCaptured, mock);
      }

      private boolean captureInstance(@Nullable Object fieldOwner, @NotNull Object instance)
      {
         if (instancesCaptured.size() < typeMetadata.getMaxInstancesToCapture()) {
            if (fieldOwner != null && typeMetadata.field != null && originalMockInstance == null) {
               originalMockInstance = getFieldValue(typeMetadata.field, fieldOwner);
            }

            instancesCaptured.add(instance);
            return true;
         }

         return false;
      }

      void reset()
      {
         originalMockInstance = null;
         instancesCaptured.clear();
      }
   }

   @NotNull final Map, List> baseTypeToCaptures;
   @NotNull private MockedType typeMetadata;

   CaptureOfNewInstances() { baseTypeToCaptures = new HashMap, List>(); }

   @Override
   @NotNull
   public final ClassVisitor createModifier(
      @Nullable ClassLoader cl, @NotNull ClassReader cr, @NotNull String baseTypeDesc)
   {
      ExpectationsModifier modifier = new ExpectationsModifier(cl, cr, typeMetadata);
      modifier.setClassNameForCapturedInstanceMethods(baseTypeDesc);

      if (typeMetadata.injectable) {
         modifier.useDynamicMockingForInstanceMethods(typeMetadata);
      }

      return modifier;
   }

   final void registerCaptureOfNewInstances(@NotNull MockedType typeMetadata, @Nullable Object mockInstance)
   {
      this.typeMetadata = typeMetadata;

      Class baseType = typeMetadata.getClassType();

      if (!typeMetadata.isFinalFieldOrParameter()) {
         makeSureAllSubtypesAreModified(baseType, typeMetadata.fieldFromTestClass);
      }

      List captures = baseTypeToCaptures.get(baseType);

      if (captures == null) {
         captures = new ArrayList();
         baseTypeToCaptures.put(baseType, captures);
      }

      captures.add(new Capture(typeMetadata, mockInstance));
   }

   public final boolean captureNewInstance(@Nullable Object fieldOwner, @NotNull Object mock)
   {
      Class mockedClass = mock.getClass();
      List captures = baseTypeToCaptures.get(mockedClass);
      boolean constructorModifiedForCaptureOnly = captures == null;

      if (constructorModifiedForCaptureOnly) {
         captures = findCaptures(mockedClass);

         if (captures == null) {
            return false;
         }
      }

      Capture captureFound = findCapture(fieldOwner, mock, captures);

      if (typeMetadata.injectable) {
         if (captureFound != null) {
            TestRun.getExecutingTest().addCapturedInstanceForInjectableMock(captureFound.originalMockInstance, mock);
         }

         constructorModifiedForCaptureOnly = true;
      }
      else if (captureFound != null) {
         TestRun.getExecutingTest().addCapturedInstance(captureFound.originalMockInstance, mock);
      }

      return constructorModifiedForCaptureOnly;
   }

   @Nullable private List findCaptures(@NotNull Class mockedClass)
   {
      Class[] interfaces = mockedClass.getInterfaces();

      for (Class anInterface : interfaces) {
         List found = baseTypeToCaptures.get(anInterface);

         if (found != null) {
            return found;
         }
      }

      Class superclass = mockedClass.getSuperclass();

      if (superclass == Object.class) {
         return null;
      }

      List found = baseTypeToCaptures.get(superclass);

      return found != null ? found : findCaptures(superclass);
   }

   @Nullable
   private Capture findCapture(@Nullable Object fieldOwner, @NotNull Object mock, @NotNull List captures)
   {
      for (Capture capture : captures) {
         if (capture.isInstanceAlreadyCaptured(mock)) {
            break;
         }
         else if (capture.captureInstance(fieldOwner, mock)) {
            return capture;
         }
      }

      return null;
   }

   @Override
   public final void cleanUp()
   {
      super.cleanUp();
      baseTypeToCaptures.clear();
   }
}