New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issue #1222 and #584 #1224
Fix issue #1222 and #584 #1224
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,14 @@ | |
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.EmptyStackException; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.regex.Pattern; | ||
import org.mockito.exceptions.misusing.InvalidUseOfMatchersException; | ||
import org.mockito.internal.matchers.Any; | ||
import org.mockito.internal.matchers.Contains; | ||
import org.mockito.internal.matchers.EndsWith; | ||
|
@@ -22,6 +24,7 @@ | |
import org.mockito.internal.matchers.Null; | ||
import org.mockito.internal.matchers.Same; | ||
import org.mockito.internal.matchers.StartsWith; | ||
import org.mockito.internal.matchers.TreatVarargsAsArray; | ||
import org.mockito.internal.matchers.apachecommons.ReflectionEquals; | ||
import org.mockito.internal.util.Primitives; | ||
|
||
|
@@ -1317,6 +1320,24 @@ public static double doubleThat(ArgumentMatcher<Double> matcher) { | |
return 0; | ||
} | ||
|
||
/** | ||
* Indicates that the varargs should be matched as an array rather than individual values. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: "individual values" -> "individual value" |
||
* | ||
* The value must be specified as a call to a method for matching an argument, e.g. | ||
* {@link #eq(Object)} or {@link ArgumentCaptor#capture()}. Failure to do so will result in | ||
* either an {@link EmptyStackException} or an {@link InvalidUseOfMatchersException}. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a unit test for this behavior? Let's see how the unit test looks like and what is the user experience. If the user experience is not great, can we offer a clean exception describing API misuse? |
||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add code sample to the Javadoc in a similar way we do it for other main public methods? |
||
* @param value expected to be a matcher such as {@code eq(...) or | ||
* {@code captor.capture()} | ||
* @param <T> the type of the vararg or array of varargs | ||
* @return {@code null} | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feature is really cool and it deserves new minor version. Can you add "since" tag to the Javadoc with the next minor version? Also, let's update the "version.properties" file accordingly! |
||
public static <T> T varargsAsArray(T value) { | ||
ArgumentMatcher nestedMatcher = mockingProgress().getArgumentMatcherStorage().popMatcher(); | ||
reportMatcher(new TreatVarargsAsArray<Object>(nestedMatcher)); | ||
return null; | ||
} | ||
|
||
private static void reportMatcher(ArgumentMatcher<?> matcher) { | ||
mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,13 +6,15 @@ | |
|
||
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; | ||
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER; | ||
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT; | ||
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_EXPANDED_ARGUMENT; | ||
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_RAW_ARGUMENT; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.mockito.ArgumentMatcher; | ||
import org.mockito.internal.matchers.CapturingMatcher; | ||
import org.mockito.internal.matchers.TreatVarargsAsArray; | ||
import org.mockito.internal.matchers.VarargMatcher; | ||
import org.mockito.invocation.Invocation; | ||
|
||
|
@@ -74,7 +76,7 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { | |
if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS) | ||
return false; | ||
|
||
Object[] arguments = invocation.getArguments(); | ||
Object[] arguments = matchingType.getArguments(invocation); | ||
for (int i = 0; i < arguments.length; i++) { | ||
ArgumentMatcher<?> matcher = matchers.get(i); | ||
Object argument = arguments[i]; | ||
|
@@ -91,21 +93,35 @@ private static MatcherApplicationType getMatcherApplicationType(Invocation invoc | |
final int expandedArguments = invocation.getArguments().length; | ||
final int matcherCount = matchers.size(); | ||
|
||
boolean treatVarargsAsArray = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow. I'm very impressed that you were able to inject the new algorithm here. The code is pretty complex around here. I'd love to see it refactored at some point in the future. It is complex but you have added fantastic test coverage that I was not able to break down :) |
||
boolean reuseLastMatcherForVarargs = false; | ||
if (matcherCount > 0 && invocation.getMethod().isVarArgs()) { | ||
ArgumentMatcher<?> lastMatcher = lastMatcher(matchers); | ||
if (lastMatcher instanceof TreatVarargsAsArray) { | ||
treatVarargsAsArray = true; | ||
} else if (lastMatcher instanceof VarargMatcher) { | ||
reuseLastMatcherForVarargs = true; | ||
} | ||
} | ||
|
||
if (expandedArguments == matcherCount) { | ||
return ONE_MATCHER_PER_ARGUMENT; | ||
return treatVarargsAsArray | ||
? ONE_MATCHER_PER_RAW_ARGUMENT : ONE_MATCHER_PER_EXPANDED_ARGUMENT; | ||
} | ||
|
||
if (rawArguments == matcherCount && isLastMatcherVargargMatcher(matchers)) { | ||
return MATCH_EACH_VARARGS_WITH_LAST_MATCHER; | ||
if (rawArguments == matcherCount) { | ||
if (treatVarargsAsArray) { | ||
return ONE_MATCHER_PER_RAW_ARGUMENT; | ||
} | ||
|
||
if (reuseLastMatcherForVarargs) { | ||
return MATCH_EACH_VARARGS_WITH_LAST_MATCHER; | ||
} | ||
} | ||
|
||
return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; | ||
} | ||
|
||
private static boolean isLastMatcherVargargMatcher(final List<ArgumentMatcher<?>> matchers) { | ||
return lastMatcher(matchers) instanceof VarargMatcher; | ||
} | ||
|
||
private static List<ArgumentMatcher<?>> appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher) { | ||
ArgumentMatcher<?> lastMatcher = lastMatcher(matchers); | ||
|
||
|
@@ -127,6 +143,18 @@ private static ArgumentMatcher<?> lastMatcher(List<ArgumentMatcher<?>> matchers) | |
} | ||
|
||
enum MatcherApplicationType { | ||
ONE_MATCHER_PER_ARGUMENT, MATCH_EACH_VARARGS_WITH_LAST_MATCHER, ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; | ||
ONE_MATCHER_PER_EXPANDED_ARGUMENT, | ||
ONE_MATCHER_PER_RAW_ARGUMENT() { | ||
@Override | ||
Object[] getArguments(Invocation invocation) { | ||
return invocation.getRawArguments(); | ||
} | ||
}, | ||
MATCH_EACH_VARARGS_WITH_LAST_MATCHER, | ||
ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; | ||
|
||
Object[] getArguments(Invocation invocation) { | ||
return invocation.getArguments(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright (c) 2017 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
package org.mockito.internal.matchers; | ||
|
||
import org.mockito.ArgumentMatcher; | ||
import org.mockito.internal.matchers.text.MatcherToString; | ||
import java.io.Serializable; | ||
|
||
/** | ||
* Wraps an {@link ArgumentMatcher} and indicates that it should be passed the varargs as a single | ||
* array rather than individual values. | ||
*/ | ||
public class TreatVarargsAsArray<T> | ||
implements ArgumentMatcher<T>, ContainsExtraTypeInfo, CapturesArguments, Serializable { | ||
|
||
private final ArgumentMatcher<? super T> delegate; | ||
|
||
public TreatVarargsAsArray(ArgumentMatcher<? super T> delegate) { | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public boolean matches(T argument) { | ||
return delegate.matches(argument); | ||
} | ||
|
||
@Override | ||
public void captureFrom(Object argument) { | ||
if (delegate instanceof CapturesArguments) { | ||
((CapturesArguments) delegate).captureFrom(argument); | ||
} | ||
} | ||
|
||
@Override | ||
public String toStringWithType() { | ||
if (delegate instanceof ContainsExtraTypeInfo) { | ||
String delagateAsString = ((ContainsExtraTypeInfo) delegate).toStringWithType(); | ||
return wrapString(delagateAsString); | ||
} | ||
return toString(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
String delegateAsString = MatcherToString.toString(delegate); | ||
return wrapString(delegateAsString); | ||
} | ||
|
||
private String wrapString(String delegateAsString) { | ||
return "varargsAsArray(" + delegateAsString + ")"; | ||
} | ||
|
||
@Override | ||
public boolean typeMatches(Object target) { | ||
if (delegate instanceof ContainsExtraTypeInfo) { | ||
return ((ContainsExtraTypeInfo) delegate).typeMatches(target); | ||
} | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,4 +26,5 @@ public interface ArgumentMatcherStorage { | |
|
||
void reset(); | ||
|
||
ArgumentMatcher<?> popMatcher(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package org.mockito.internal.matchers; | ||
|
||
import org.junit.Test; | ||
|
||
import static junit.framework.TestCase.assertTrue; | ||
import static org.junit.Assert.assertArrayEquals; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertFalse; | ||
|
||
public class TreatVarargsAsArrayTest { | ||
|
||
@Test | ||
public void delegateDoesNotImplementCapturesArgument_captureFrom() { | ||
TreatVarargsAsArray<String[]> matcher = new TreatVarargsAsArray<String[]>( | ||
new Equals(new String[] {"1"})); | ||
|
||
// Should not do anything. | ||
matcher.captureFrom("2"); | ||
} | ||
|
||
@Test | ||
public void delegateDoesImplementCapturesArgument_captureFrom() { | ||
CapturingMatcher<String[]> capturingMatcher = new CapturingMatcher<String[]>(); | ||
TreatVarargsAsArray<String[]> matcher = | ||
new TreatVarargsAsArray<String[]>(capturingMatcher); | ||
|
||
// Should not do anything. | ||
matcher.captureFrom(new String[] {"2"}); | ||
|
||
assertArrayEquals(new String[] {"2"}, capturingMatcher.getLastValue()); | ||
} | ||
|
||
@Test | ||
public void delegateDoesNotImplementContainsExtraTypeInfo_toStringWithType() { | ||
TreatVarargsAsArray<String[]> matcher = new TreatVarargsAsArray<String[]>(NotNull.NOT_NULL); | ||
|
||
assertEquals("varargsAsArray(notNull())", matcher.toStringWithType()); | ||
} | ||
|
||
@Test | ||
public void delegateDoesImplementContainsExtraTypeInfo_toStringWithType() { | ||
TreatVarargsAsArray<String[]> matcher = new TreatVarargsAsArray<String[]>( | ||
new Equals(new String[] {"1"})); | ||
|
||
assertEquals("varargsAsArray((String[]) [\"1\"])", matcher.toStringWithType()); | ||
} | ||
|
||
@Test | ||
public void delegateDoesNotImplementContainsExtraTypeInfo_typeMatches() { | ||
TreatVarargsAsArray<String[]> matcher = new TreatVarargsAsArray<String[]>(NotNull.NOT_NULL); | ||
|
||
assertTrue(matcher.typeMatches(new String[] {})); | ||
assertTrue(matcher.typeMatches(1)); | ||
} | ||
|
||
@Test | ||
public void delegateDoesImplementContainsExtraTypeInfo_typeMatches() { | ||
TreatVarargsAsArray<String[]> matcher = new TreatVarargsAsArray<String[]>( | ||
new Equals(new String[] {"1"})); | ||
|
||
assertTrue("String[]", matcher.typeMatches(new String[] {})); | ||
assertFalse("int", matcher.typeMatches(1)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a new point to the main Mockito Javadoc with a short description of the feature? This is a really cool feature and it deserves a note in main Javadoc. That doc acts as Mockito user guide and also shows notable features. Feel free to bump new minor version.