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.

There is a newer version: 1.7
Show 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 mockit.external.asm4.*;
import mockit.internal.capturing.*;
import mockit.internal.state.*;
import mockit.internal.util.*;

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

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

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

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

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

            instancesCaptured.add(instance);
            return true;
         }

         return false;
      }

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

   final Map, List> baseTypeToCaptures;
   private MockedType typeMetadata;
   Capture captureFound;

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

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

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

      return modifier;
   }

   final void registerCaptureOfNewInstances(MockedType typeMetadata, 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));
   }

   final boolean captureNewInstance(Object fieldOwner, Object mock)
   {
      captureFound = null;

      Class mockedClass = mock.getClass();
      List captures = baseTypeToCaptures.get(mockedClass);
      boolean constructorModifiedForCaptureOnly = captures == null;

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

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

      for (Capture capture : captures) {
         if (capture.isInstanceAlreadyCaptured(mock)) {
            break;
         }
         else if (capture.captureInstance(fieldOwner, mock)) {
            captureFound = capture;
            break;
         }
      }

      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;
   }

   private List findCaptures(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);
   }

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