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

mockit.internal.faking.FakeState Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains mocking/faking APIs and a code coverage tool, 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.49
Show newest version
/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.faking;

import java.lang.reflect.*;
import javax.annotation.*;

import mockit.internal.faking.FakeMethods.*;
import mockit.internal.reflection.*;
import mockit.internal.util.*;

final class FakeState
{
   private static final ClassLoader THIS_CL = FakeState.class.getClassLoader();

   @Nonnull final FakeMethod fakeMethod;
   @Nullable private Method actualFakeMethod;
   @Nullable private Member realMethodOrConstructor;
   @Nullable private Object realClass;

   // Current fake invocation state:
   private int invocationCount;
   @Nullable private ThreadLocal proceedingInvocation;

   // Helper field just for synchronization:
   @Nonnull private final Object invocationCountLock;

   FakeState(@Nonnull FakeMethod fakeMethod) {
      this.fakeMethod = fakeMethod;
      invocationCountLock = new Object();

      if (fakeMethod.canBeReentered()) {
         makeReentrant();
      }
   }

   @Nonnull Class getRealClass() { return fakeMethod.getRealClass(); }

   private void makeReentrant() { proceedingInvocation = new ThreadLocal<>(); }

   boolean update() {
      if (proceedingInvocation != null) {
         FakeInvocation invocation = proceedingInvocation.get();

         if (invocation != null && invocation.proceeding) {
            invocation.proceeding = false;
            return false;
         }
      }

      synchronized (invocationCountLock) {
         invocationCount++;
      }

      return true;
   }

   int getTimesInvoked() {
      synchronized (invocationCountLock) {
         return invocationCount;
      }
   }

   @Nonnull
   Member getRealMethodOrConstructor(@Nonnull String fakedClassDesc, @Nonnull String fakedMethodName, @Nonnull String fakedMethodDesc) {
      Class fakedClass = ClassLoad.loadFromLoader(THIS_CL, fakedClassDesc.replace('/', '.'));
      return getRealMethodOrConstructor(fakedClass, fakedMethodName, fakedMethodDesc);
   }

   @Nonnull
   Member getRealMethodOrConstructor(@Nonnull Class fakedClass, @Nonnull String fakedMethodName, @Nonnull String fakedMethodDesc) {
      Member member = realMethodOrConstructor;

      if (member == null || !fakedClass.equals(realClass)) {
         String memberName = "$init".equals(fakedMethodName) ? "" : fakedMethodName;

         RealMethodOrConstructor realMember;
         try { realMember = new RealMethodOrConstructor(fakedClass, memberName, fakedMethodDesc); }
         catch (NoSuchMethodException e) { throw new RuntimeException(e); }

         member = realMember.getMember();

         if (!fakeMethod.isAdvice) {
            realMethodOrConstructor = member;
            realClass = fakedClass;
         }
      }

      return member;
   }

   boolean shouldProceedIntoRealImplementation(@Nullable Object fake, @Nonnull String classDesc) {
      if (proceedingInvocation != null) {
         FakeInvocation pendingInvocation = proceedingInvocation.get();

         //noinspection RedundantIfStatement
         if (pendingInvocation != null && pendingInvocation.isMethodInSuperclass(fake, classDesc)) {
            return true;
         }
      }

      return false;
   }

   void prepareToProceed(@Nonnull FakeInvocation invocation) {
      if (proceedingInvocation == null) {
         throw new UnsupportedOperationException("Cannot proceed into abstract/interface method");
      }

      if (fakeMethod.isForNativeMethod()) {
         throw new UnsupportedOperationException("Cannot proceed into real implementation of native method");
      }

      FakeInvocation previousInvocation = proceedingInvocation.get();

      if (previousInvocation != null) {
         invocation.setPrevious(previousInvocation);
      }

      proceedingInvocation.set(invocation);
   }

   void prepareToProceedFromNonRecursiveFake(@Nonnull FakeInvocation invocation) {
      assert proceedingInvocation != null;
      proceedingInvocation.set(invocation);
   }

   void clearProceedIndicator() {
      assert proceedingInvocation != null;
      FakeInvocation currentInvocation = proceedingInvocation.get();
      FakeInvocation previousInvocation = (FakeInvocation) currentInvocation.getPrevious();
      proceedingInvocation.set(previousInvocation);
   }

   @Nonnull
   Method getFakeMethod(@Nonnull Class fakeClass, @Nonnull Class[] parameterTypes) {
      if (actualFakeMethod == null) {
         actualFakeMethod = MethodReflection.findCompatibleMethod(fakeClass, fakeMethod.name, parameterTypes);
      }

      return actualFakeMethod;
   }

   @Override @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
   public boolean equals(@Nonnull Object other) { return fakeMethod.equals(((FakeState) other).fakeMethod); }

   @Override
   public int hashCode() { return fakeMethod.hashCode(); }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy