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

mockit.internal.expectations.ReplayPhase 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.53.0
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.expectations;

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

import mockit.internal.expectations.invocation.*;

final class ReplayPhase extends Phase
{
   @Nonnull final FailureState failureState;
   @Nonnull final List invocations;
   @Nonnull final List invocationInstances;
   @Nonnull final List invocationArguments;

   ReplayPhase(@Nonnull PhasedExecutionState executionState, @Nonnull FailureState failureState) {
      super(executionState);
      this.failureState = failureState;
      invocations = new ArrayList<>();
      invocationInstances = new ArrayList<>();
      invocationArguments = new ArrayList<>();
   }

   @Override @Nullable
   Object handleInvocation(
      @Nullable Object mock, int mockAccess, @Nonnull String mockClassDesc, @Nonnull String mockNameAndDesc,
      @Nullable String genericSignature, boolean withRealImpl, @Nonnull Object[] args
   ) throws Throwable {
      Expectation expectation = executionState.findExpectation(mock, mockClassDesc, mockNameAndDesc, args);
      Object replacementInstance = mock == null ?
         null : executionState.equivalentInstances.getReplacementInstanceForMethodInvocation(mock, mockNameAndDesc);

      if (expectation == null) {
         expectation = createExpectation(
            replacementInstance == null ? mock : replacementInstance, mockAccess, mockClassDesc, mockNameAndDesc, genericSignature, args);
      }
      else if (expectation.recordPhase != null) {
         registerNewInstanceAsEquivalentToOneFromRecordedConstructorInvocation(mock, expectation.invocation);
      }

      invocations.add(expectation);
      invocationInstances.add(mock);
      invocationArguments.add(args);
      expectation.constraints.incrementInvocationCount();

      return produceResult(expectation, mock, withRealImpl, args);
   }

   @Nonnull
   private Expectation createExpectation(
      @Nullable Object mock, int mockAccess, @Nonnull String mockClassDesc, @Nonnull String mockNameAndDesc,
      @Nullable String genericSignature, @Nonnull Object[] args
   ) {
      ExpectedInvocation invocation =
         new ExpectedInvocation(mock, mockAccess, mockClassDesc, mockNameAndDesc, false, genericSignature, args);
      Expectation expectation = new Expectation(invocation);
      executionState.addExpectation(expectation);
      return expectation;
   }

   private void registerNewInstanceAsEquivalentToOneFromRecordedConstructorInvocation(
      @Nullable Object mock, @Nonnull ExpectedInvocation invocation
   ) {
      if (mock != null && invocation.isConstructor()) {
         Map instanceMap = getInstanceMap();
         instanceMap.put(mock, invocation.instance);
      }
   }

   @Nullable
   private Object produceResult(
      @Nonnull Expectation expectation, @Nullable Object mock, boolean withRealImpl, @Nonnull Object[] args
   ) throws Throwable {
      boolean executeRealImpl = withRealImpl && expectation.recordPhase == null;

      if (executeRealImpl) {
         expectation.executedRealImplementation = true;
         return Void.class;
      }

      if (expectation.constraints.isInvocationCountMoreThanMaximumExpected()) {
         UnexpectedInvocation unexpectedInvocation = expectation.invocation.errorForUnexpectedInvocation(args);
         failureState.setErrorThrown(unexpectedInvocation);
         return null;
      }

      return expectation.produceResult(mock, args);
   }

   @Nullable
   Error endExecution() {
      Error missingInvocation = getErrorForFirstExpectationThatIsMissing();
      return missingInvocation;
   }

   @Nullable
   private Error getErrorForFirstExpectationThatIsMissing() {
      List notStrictExpectations = executionState.expectations;

      // New expectations might get added to the list, so a regular loop would cause a CME.
      for (int i = 0, n = notStrictExpectations.size(); i < n; i++) {
         Expectation notStrict = notStrictExpectations.get(i);
         InvocationConstraints constraints = notStrict.constraints;

         if (constraints.isInvocationCountLessThanMinimumExpected()) {
            List nonMatchingInvocations = getNonMatchingInvocations(notStrict);
            return constraints.errorForMissingExpectations(notStrict.invocation, nonMatchingInvocations);
         }
      }

      return null;
   }

   @Nonnull
   private List getNonMatchingInvocations(@Nonnull Expectation unsatisfiedExpectation) {
      ExpectedInvocation unsatisfiedInvocation = unsatisfiedExpectation.invocation;
      List nonMatchingInvocations = new ArrayList<>();

      for (Expectation replayedExpectation : invocations) {
         ExpectedInvocation replayedInvocation = replayedExpectation.invocation;

         if (replayedExpectation != unsatisfiedExpectation && replayedInvocation.isMatch(unsatisfiedInvocation)) {
            nonMatchingInvocations.add(replayedInvocation);
         }
      }

      return nonMatchingInvocations;
   }
}