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

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

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
/*
 * Copyright (c) 2006 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.annotation.*;
import java.lang.reflect.*;
import javax.annotation.*;
import static java.lang.reflect.Modifier.*;

import mockit.*;
import mockit.internal.expectations.state.*;
import mockit.internal.injection.*;
import mockit.internal.reflection.*;
import mockit.internal.state.*;
import mockit.internal.util.*;

@SuppressWarnings("EqualsAndHashcode")
public final class MockedType extends InjectionProvider
{
   @Mocked private static final Object DUMMY = null;
   private static final int DUMMY_HASHCODE;

   static
   {
      int h = 0;

      try {
         Field dummy = MockedType.class.getDeclaredField("DUMMY");
         Mocked mocked = dummy.getAnnotation(Mocked.class);
         h = mocked.hashCode();
      }
      catch (NoSuchFieldException ignore) {}

      DUMMY_HASHCODE = h;
   }

   @Nullable public final Field field;
   public final boolean fieldFromTestClass;
   private final int accessModifiers;
   @Nullable private final Mocked mocked;
   @Nullable private final Capturing capturing;
   @Nullable private final Class parameterImplementationClass;
   public final boolean injectable;
   @Nullable Object providedValue;

   public MockedType(@Nonnull Field field)
   {
      super(field.getGenericType(), field.getName());
      this.field = field;
      fieldFromTestClass = true;
      accessModifiers = field.getModifiers();
      mocked = field.getAnnotation(Mocked.class);
      capturing = field.getAnnotation(Capturing.class);
      parameterImplementationClass = null;
      Injectable injectableAnnotation = field.getAnnotation(Injectable.class);
      injectable = injectableAnnotation != null;
      providedValue = getProvidedInjectableValue(injectableAnnotation);
      registerCascadingAsNeeded();
   }

   @Nullable
   private Object getProvidedInjectableValue(@Nullable Injectable annotation)
   {
      if (annotation != null) {
         String value = annotation.value();

         if (!value.isEmpty()) {
            Class injectableClass = getClassType();

            if (injectableClass == TypeVariable.class) {
               // Not supported, do nothing.
            }
            else {
               return Utilities.convertFromString(injectableClass, value);
            }
         }
      }

      return null;
   }

   private void registerCascadingAsNeeded()
   {
      if (isMockableType()) {
         Type mockedType = declaredType;

         if (!(mockedType instanceof TypeVariable)) {
            ExecutingTest executingTest = TestRun.getExecutingTest();
            CascadingTypes types = executingTest.getCascadingTypes();
            types.add(fieldFromTestClass, mockedType);
         }
      }
   }

   MockedType(
      @Nonnull TestMethod testMethod, @Nonnegative int paramIndex,
      @Nonnull Type parameterType, @Nonnull Annotation[] annotationsOnParameter,
      @Nullable Class parameterImplementationClass)
   {
      super(parameterType, ParameterNames.getName(testMethod, paramIndex));
      field = null;
      fieldFromTestClass = false;
      accessModifiers = 0;
      mocked = getAnnotation(annotationsOnParameter, Mocked.class);
      capturing = getAnnotation(annotationsOnParameter, Capturing.class);
      this.parameterImplementationClass = parameterImplementationClass;
      Injectable injectableAnnotation = getAnnotation(annotationsOnParameter, Injectable.class);
      injectable = injectableAnnotation != null;
      providedValue = getProvidedInjectableValue(injectableAnnotation);

      if (providedValue == null && parameterType instanceof Class) {
         Class parameterClass = (Class) parameterType;

         if (parameterClass.isPrimitive()) {
            providedValue = DefaultValues.defaultValueForPrimitiveType(parameterClass);
         }
      }

      registerCascadingAsNeeded();
   }

   @Nullable
   private static  A getAnnotation(
      @Nonnull Annotation[] annotations, @Nonnull Class annotation)
   {
      for (Annotation paramAnnotation : annotations) {
         if (paramAnnotation.annotationType() == annotation) {
            //noinspection unchecked
            return (A) paramAnnotation;
         }
      }

      return null;
   }

   MockedType(@Nonnull String cascadingMethodName, @Nonnull Type cascadedType)
   {
      super(cascadedType, cascadingMethodName);
      field = null;
      fieldFromTestClass = false;
      accessModifiers = 0;
      mocked = null;
      capturing = null;
      injectable = true;
      parameterImplementationClass = null;
   }

   @Nonnull @Override public Class getClassOfDeclaredType() { return getClassType(); }

   /**
    * @return the class object corresponding to the type to be mocked, or TypeVariable.class in case the
    * mocked type is a type variable (which usually occurs when the mocked implements/extends multiple types)
    */
   @Nonnull
   public Class getClassType()
   {
      if (parameterImplementationClass != null) {
         return parameterImplementationClass;
      }

      Type mockedType = declaredType;

      if (mockedType instanceof Class) {
         return (Class) mockedType;
      }

      if (mockedType instanceof ParameterizedType) {
         ParameterizedType parameterizedType = (ParameterizedType) mockedType;
         return (Class) parameterizedType.getRawType();
      }

      // Occurs when declared type is a TypeVariable, usually having two or more bound types.
      // In such cases, there isn't a single class type.
      return TypeVariable.class;
   }

   boolean isMockableType()
   {
      if (mocked == null && !injectable && capturing == null) {
         return false;
      }

      Type mockedType = declaredType;

      if (!(mockedType instanceof Class)) {
         return true;
      }

      Class classType = (Class) mockedType;

      if (isUnmockableJREType(classType)) {
         return false;
      }

      if (injectable) {
         if (isJREValueType(classType) || classType.isEnum()) {
            return false;
         }
      }

      return true;
   }

   private static boolean isUnmockableJREType(@Nonnull Class type)
   {
      return type.isPrimitive() || type.isArray() || type == Integer.class;
   }

   private static boolean isJREValueType(@Nonnull Class type)
   {
      return
         type == String.class || type == Boolean.class || type == Character.class ||
         Number.class.isAssignableFrom(type);
   }

   boolean isFinalFieldOrParameter() { return field == null || isFinal(accessModifiers); }
   boolean isClassInitializationToBeStubbedOut() { return mocked != null && mocked.stubOutClassInitialization(); }

   boolean withInstancesToCapture() { return getMaxInstancesToCapture() > 0; }
   int getMaxInstancesToCapture() { return capturing == null ? 0 : capturing.maxInstances(); }

   @Nullable @Override
   public Object getValue(@Nullable Object owner)
   {
      if (field == null) {
         return providedValue;
      }

      Object value = FieldReflection.getFieldValue(field, owner);

      if (!injectable) {
         return value;
      }

      Class fieldType = field.getType();

      if (value == null) {
         if (providedValue != null) {
            return providedValue;
         }

         if (isFinalFieldOrParameter()) {
            return NULL;
         }

         if (fieldType == String.class) {
            return "";
         }

         return null;
      }

      if (providedValue == null) {
         return value;
      }

      if (!fieldType.isPrimitive()) {
         return value;
      }

      Object defaultValue = DefaultValues.defaultValueForPrimitiveType(fieldType);

      return value.equals(defaultValue) ? providedValue : value;
   }

   @Override
   public int hashCode()
   {
      int result = declaredType.hashCode();

      if (isFinal(accessModifiers)) {
         result *= 31;
      }

      if (injectable) {
         result *= 37;
      }

      if (mocked != null) {
         int h = mocked.hashCode();

         if (h != DUMMY_HASHCODE) {
            result = 31 * result + h;
         }
      }

      return result;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy