• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.invocation;
6 
7 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
8 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
9 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT;
10 
11 import java.util.ArrayList;
12 import java.util.List;
13 
14 import org.mockito.ArgumentMatcher;
15 import org.mockito.internal.hamcrest.HamcrestArgumentMatcher;
16 import org.mockito.internal.matchers.CapturingMatcher;
17 import org.mockito.internal.matchers.VarargMatcher;
18 import org.mockito.invocation.Invocation;
19 
20 public class MatcherApplicationStrategy {
21 
22     private final Invocation invocation;
23     private final List<ArgumentMatcher<?>> matchers;
24     private final MatcherApplicationType matchingType;
25 
26 
27 
MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType)28     private MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType) {
29         this.invocation = invocation;
30         if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) {
31             int times = varargLength(invocation);
32             this.matchers = appendLastMatcherNTimes(matchers, times);
33         } else {
34             this.matchers = matchers;
35         }
36 
37         this.matchingType = matchingType;
38     }
39 
40     /**
41      * Returns the {@link MatcherApplicationStrategy} that must be used to capture the
42      * arguments of the given <b>invocation</b> using the given <b>matchers</b>.
43      *
44      * @param invocation
45      *            that contain the arguments to capture
46      * @param matchers
47      *            that will be used to capture the arguments of the invocation,
48      *            the passed {@link List} is not required to contain a
49      *            {@link CapturingMatcher}
50      * @return never <code>null</code>
51      */
getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers)52     public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
53 
54         MatcherApplicationType type = getMatcherApplicationType(invocation, matchers);
55         return new MatcherApplicationStrategy(invocation, matchers, type);
56     }
57 
58     /**
59      * Applies the given {@link ArgumentMatcherAction} to all arguments and
60      * corresponding matchers
61      *
62      * @param action
63      *            must not be <code>null</code>
64      * @return
65      *         <ul>
66      *         <li><code>true</code> if the given <b>action</b> returned
67      *         <code>true</code> for all arguments and matchers passed to it.
68      *         <li><code>false</code> if the given <b>action</b> returned
69      *         <code>false</code> for one of the passed arguments and matchers
70      *         <li><code>false</code> if the given matchers don't fit to the given invocation
71      *         because too many or to few matchers are available.
72      *         </ul>
73      */
forEachMatcherAndArgument(ArgumentMatcherAction action)74     public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
75         if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS)
76             return false;
77 
78         Object[] arguments = invocation.getArguments();
79         for (int i = 0; i < arguments.length; i++) {
80             ArgumentMatcher<?> matcher = matchers.get(i);
81             Object argument = arguments[i];
82 
83             if (!action.apply(matcher, argument)) {
84                 return false;
85             }
86         }
87         return true;
88     }
89 
getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers)90     private static MatcherApplicationType getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
91         final int rawArguments = invocation.getRawArguments().length;
92         final int expandedArguments = invocation.getArguments().length;
93         final int matcherCount = matchers.size();
94 
95         if (expandedArguments == matcherCount) {
96             return ONE_MATCHER_PER_ARGUMENT;
97         }
98 
99         if (rawArguments == matcherCount && isLastMatcherVarargMatcher(matchers)) {
100             return MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
101         }
102 
103         return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
104     }
105 
isLastMatcherVarargMatcher(final List<ArgumentMatcher<?>> matchers)106     private static boolean isLastMatcherVarargMatcher(final List<ArgumentMatcher<?>> matchers) {
107         ArgumentMatcher<?> argumentMatcher = lastMatcher(matchers);
108         if (argumentMatcher instanceof HamcrestArgumentMatcher<?>) {
109            return  ((HamcrestArgumentMatcher<?>) argumentMatcher).isVarargMatcher();
110         }
111         return argumentMatcher instanceof VarargMatcher;
112     }
113 
appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher)114     private static List<ArgumentMatcher<?>> appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher) {
115         ArgumentMatcher<?> lastMatcher = lastMatcher(matchers);
116 
117         List<ArgumentMatcher<?>> expandedMatchers = new ArrayList<ArgumentMatcher<?>>(matchers);
118         for (int i = 0; i < timesToAppendLastMatcher; i++) {
119             expandedMatchers.add(lastMatcher);
120         }
121         return expandedMatchers;
122     }
123 
varargLength(Invocation invocation)124     private static int varargLength(Invocation invocation) {
125         int rawArgumentCount = invocation.getRawArguments().length;
126         int expandedArgumentCount = invocation.getArguments().length;
127         return expandedArgumentCount - rawArgumentCount;
128     }
129 
lastMatcher(List<ArgumentMatcher<?>> matchers)130     private static ArgumentMatcher<?> lastMatcher(List<ArgumentMatcher<?>> matchers) {
131         return matchers.get(matchers.size() - 1);
132     }
133 
134     enum MatcherApplicationType {
135         ONE_MATCHER_PER_ARGUMENT, MATCH_EACH_VARARGS_WITH_LAST_MATCHER, ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
136     }
137 }
138