Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright 2001-2009 OFFIS, Tammo Freese
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package org.easymock.internal;
     17 
     18 import java.io.Serializable;
     19 import java.lang.reflect.Method;
     20 import java.util.ArrayList;
     21 import java.util.List;
     22 
     23 import org.easymock.EasyMock;
     24 
     25 public class MocksBehavior implements IMocksBehavior, Serializable {
     26 
     27     private static final long serialVersionUID = 3265727009370529027L;
     28 
     29     private final List<UnorderedBehavior> behaviorLists = new ArrayList<UnorderedBehavior>();
     30 
     31     private final List<ExpectedInvocationAndResult> stubResults = new ArrayList<ExpectedInvocationAndResult>();
     32 
     33     private final boolean nice;
     34 
     35     private boolean checkOrder;
     36 
     37     private boolean isThreadSafe;
     38 
     39     private boolean shouldBeUsedInOneThread;
     40 
     41     private int position = 0;
     42 
     43     private transient volatile Thread lastThread;
     44 
     45     private LegacyMatcherProvider legacyMatcherProvider;
     46 
     47     public MocksBehavior(boolean nice) {
     48         this.nice = nice;
     49         this.isThreadSafe = !Boolean.valueOf(EasyMockProperties.getInstance()
     50                 .getProperty(EasyMock.NOT_THREAD_SAFE_BY_DEFAULT));
     51         this.shouldBeUsedInOneThread = Boolean.valueOf(EasyMockProperties
     52                 .getInstance().getProperty(
     53                         EasyMock.ENABLE_THREAD_SAFETY_CHECK_BY_DEFAULT));
     54     }
     55 
     56     public final void addStub(ExpectedInvocation expected, Result result) {
     57         stubResults.add(new ExpectedInvocationAndResult(expected, result));
     58     }
     59 
     60     public void addExpected(ExpectedInvocation expected, Result result,
     61             Range count) {
     62         if (legacyMatcherProvider != null) {
     63             expected = expected.withMatcher(legacyMatcherProvider
     64                     .getMatcher(expected.getMethod()));
     65         }
     66         addBehaviorListIfNecessary(expected);
     67         lastBehaviorList().addExpected(expected, result, count);
     68     }
     69 
     70     private final Result getStubResult(Invocation actual) {
     71         for (ExpectedInvocationAndResult each : stubResults) {
     72             if (each.getExpectedInvocation().matches(actual)) {
     73                 return each.getResult();
     74             }
     75         }
     76         return null;
     77     }
     78 
     79     private void addBehaviorListIfNecessary(ExpectedInvocation expected) {
     80         if (behaviorLists.isEmpty()
     81                 || !lastBehaviorList().allowsExpectedInvocation(expected,
     82                         checkOrder)) {
     83             behaviorLists.add(new UnorderedBehavior(checkOrder));
     84         }
     85     }
     86 
     87     private UnorderedBehavior lastBehaviorList() {
     88         return behaviorLists.get(behaviorLists.size() - 1);
     89     }
     90 
     91     @SuppressWarnings("deprecation")
     92     public final Result addActual(Invocation actual) {
     93         int initialPosition = position;
     94 
     95         while (position < behaviorLists.size()) {
     96             Result result = behaviorLists.get(position).addActual(actual);
     97             if (result != null) {
     98                 return result;
     99             }
    100             if (!behaviorLists.get(position).verify()) {
    101                 break;
    102             }
    103             position++;
    104         }
    105         Result stubOrNice = getStubResult(actual);
    106         if (stubOrNice == null && nice) {
    107             stubOrNice = Result.createReturnResult(RecordState
    108                     .emptyReturnValueFor(actual.getMethod().getReturnType()));
    109         }
    110 
    111         int endPosition = position;
    112 
    113         // Do not move the cursor in case of stub, nice or error
    114         position = initialPosition;
    115 
    116         if (stubOrNice != null) {
    117             actual.validateCaptures();
    118             actual.clearCaptures();
    119             return stubOrNice;
    120         }
    121 
    122         // Case where the loop was exited at the end of the behaviorLists
    123         if (endPosition == behaviorLists.size()) {
    124             endPosition--;
    125         }
    126 
    127         // Loop all around the behaviors left to generate the message
    128         StringBuilder errorMessage = new StringBuilder(70 * (endPosition
    129                 - initialPosition + 1)); // rough approximation of the length
    130         errorMessage.append("\n  Unexpected method call ").append(
    131                 actual.toString(org.easymock.MockControl.EQUALS_MATCHER));
    132 
    133         List<ErrorMessage> messages = new ArrayList<ErrorMessage>();
    134 
    135         int matches = 0;
    136 
    137         // First find how many match we have
    138         for (int i = initialPosition; i <= endPosition; i++) {
    139             List<ErrorMessage> thisListMessages = behaviorLists.get(i)
    140                     .getMessages(actual);
    141             messages.addAll(thisListMessages);
    142             for (ErrorMessage m : thisListMessages) {
    143                 if (m.isMatching()) {
    144                     matches++;
    145                 }
    146             }
    147         }
    148 
    149         if (matches > 1) {
    150             errorMessage
    151                     .append(". Possible matches are marked with (+1):");
    152         } else {
    153             errorMessage.append(":");
    154         }
    155 
    156         for (ErrorMessage m : messages) {
    157             m.appendTo(errorMessage, matches);
    158         }
    159 
    160         // And finally throw the error
    161         throw new AssertionErrorWrapper(new AssertionError(errorMessage));
    162     }
    163 
    164     public void verify() {
    165         boolean verified = true;
    166 
    167         for (UnorderedBehavior behaviorList : behaviorLists.subList(position,
    168                 behaviorLists.size())) {
    169             if (!behaviorList.verify()) {
    170                 verified = false;
    171             }
    172         }
    173         if (verified) {
    174             return;
    175         }
    176 
    177         StringBuilder errorMessage = new StringBuilder(70 * (behaviorLists
    178                 .size()
    179                 - position + 1));
    180 
    181         errorMessage.append("\n  Expectation failure on verify:");
    182         for (UnorderedBehavior behaviorList : behaviorLists.subList(position,
    183                 behaviorLists.size())) {
    184             for (ErrorMessage m : behaviorList.getMessages(null)) {
    185                 m.appendTo(errorMessage, 0);
    186             }
    187         }
    188 
    189         throw new AssertionErrorWrapper(new AssertionError(errorMessage
    190                 .toString()));
    191     }
    192 
    193     public void checkOrder(boolean value) {
    194         this.checkOrder = value;
    195     }
    196 
    197     public void makeThreadSafe(boolean isThreadSafe) {
    198         this.isThreadSafe = isThreadSafe;
    199     }
    200 
    201     public void shouldBeUsedInOneThread(boolean shouldBeUsedInOneThread) {
    202         this.shouldBeUsedInOneThread = shouldBeUsedInOneThread;
    203     }
    204 
    205     public boolean isThreadSafe() {
    206         return this.isThreadSafe;
    207     }
    208 
    209     public void checkThreadSafety() {
    210         if (!shouldBeUsedInOneThread) {
    211             return;
    212         }
    213         if (lastThread == null) {
    214             lastThread = Thread.currentThread();
    215         } else if(lastThread != Thread.currentThread()) {
    216             throw new AssertionErrorWrapper(new AssertionError(
    217                     "\n Mock isn't supposed to be called from multiple threads. Last: "
    218                             + lastThread +
    219                     " Current: " + Thread.currentThread()));
    220         }
    221     }
    222 
    223     public LegacyMatcherProvider getLegacyMatcherProvider() {
    224         if (legacyMatcherProvider == null) {
    225             legacyMatcherProvider = new LegacyMatcherProvider();
    226         }
    227         return legacyMatcherProvider;
    228     }
    229 
    230     @SuppressWarnings("deprecation")
    231     public void setDefaultMatcher(org.easymock.ArgumentsMatcher matcher) {
    232         getLegacyMatcherProvider().setDefaultMatcher(matcher);
    233     }
    234 
    235     @SuppressWarnings("deprecation")
    236     public void setMatcher(Method method, org.easymock.ArgumentsMatcher matcher) {
    237         getLegacyMatcherProvider().setMatcher(method, matcher);
    238     }
    239 }
    240