Skip to content
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

Remove VarargMatcher #2835

Merged
merged 14 commits into from
Dec 30, 2022
2 changes: 1 addition & 1 deletion src/main/java/org/mockito/ArgumentMatchers.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public static <T> T any() {
* @see #isNull()
*/
public static <T> T any(Class<T> type) {
reportMatcher(new InstanceOf.VarArgAware(type, "<any " + type.getCanonicalName() + ">"));
reportMatcher(new InstanceOf(type, "<any " + type.getCanonicalName() + ">"));
return defaultValue(type);
}

Expand Down
50 changes: 47 additions & 3 deletions src/main/java/org/mockito/hamcrest/MockitoHamcrest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@
* Due to how java works we don't really have a clean way of detecting this scenario and protecting the user from this problem.
* Hopefully, the javadoc describes the problem and solution well.
* If you have an idea how to fix the problem, let us know via the mailing list or the issue tracker.
* <p/>
* By default, a matcher passed to a varargs parameter will match against the first element in the varargs array.
* To match against the raw varargs array pass the type of the varargs parameter to {@link MockitoHamcrest#argThat(Matcher, Class)}
* <p/>
* For example, to match any number of {@code String} values:
* <pre>
* import static org.hamcrest.CoreMatchers.isA;
* import static org.mockito.hamcrest.MockitoHamcrest.argThat;
*
* // Given:
* void varargMethod(String... args);
*
* //stubbing
* when(mock.varargMethod(argThat(isA(String[].class), String[].class));
*
* //verification
* verify(mock).giveMe(argThat(isA(String[].class), String[].class));
* </pre>
*
* @since 2.1.0
*/
Expand All @@ -62,6 +80,26 @@ public static <T> T argThat(Matcher<T> matcher) {
return (T) defaultValue(genericTypeOfMatcher(matcher.getClass()));
}

/**
* Allows matching arguments with hamcrest matchers.
* <p/>
* This variant can be used to pass an explicit {@code type},
* which can be useful to provide a matcher that matches against all
* elements in a varargs parameter.
* <p/>
* See examples in javadoc for {@link MockitoHamcrest} class
*
* @param matcher decides whether argument matches
* @param type the type the matcher matches.
* @return <code>null</code> or default value for primitive (0, false, etc.)
* @since 5.0.0
*/
@SuppressWarnings("unchecked")
public static <T> T argThat(Matcher<T> matcher, Class<T> type) {
reportMatcher(matcher, type);
return (T) defaultValue(genericTypeOfMatcher(matcher.getClass()));
}

/**
* Enables integrating hamcrest matchers that match primitive <code>char</code> arguments.
* Note that {@link #argThat} will not work with primitive <code>char</code> matchers due to <code>NullPointerException</code> auto-unboxing caveat.
Expand Down Expand Up @@ -175,9 +213,15 @@ public static double doubleThat(Matcher<Double> matcher) {
}

private static <T> void reportMatcher(Matcher<T> matcher) {
mockingProgress()
.getArgumentMatcherStorage()
.reportMatcher(new HamcrestArgumentMatcher<T>(matcher));
reportMatcher(new HamcrestArgumentMatcher<T>(matcher));
}

private static <T> void reportMatcher(Matcher<T> matcher, Class<T> type) {
reportMatcher(new HamcrestArgumentMatcher<T>(matcher, type));
}

private static <T> void reportMatcher(final HamcrestArgumentMatcher<T> matcher) {
mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher);
}

private MockitoHamcrest() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,40 @@
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;

import static java.util.Objects.requireNonNull;

public class HamcrestArgumentMatcher<T> implements ArgumentMatcher<T> {

private final Matcher matcher;
private final Matcher<T> matcher;
private final Class<?> type;

public HamcrestArgumentMatcher(Matcher<T> matcher) {
this.matcher = matcher;
this(Void.class, matcher);
}

public HamcrestArgumentMatcher(Matcher<T> matcher, Class<T> type) {
this(type, matcher);
}

private HamcrestArgumentMatcher(Class<?> type, Matcher<T> matcher) {
this.type = requireNonNull(type, "type");
this.matcher = requireNonNull(matcher, "matcher");
}

@Override
public boolean matches(Object argument) {
return this.matcher.matches(argument);
}

@SuppressWarnings("deprecation")
public boolean isVarargMatcher() {
return matcher instanceof VarargMatcher;
}

@Override
public String toString() {
// TODO SF add unit tests and integ test coverage for toString()
return StringDescription.toString(matcher);
}

@Override
public Class<?> type() {
return type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@
*/
package org.mockito.internal.invocation;

import java.util.ArrayList;
import java.util.List;

import org.mockito.ArgumentMatcher;
import org.mockito.internal.hamcrest.HamcrestArgumentMatcher;
import org.mockito.internal.matchers.CapturingMatcher;
import org.mockito.internal.matchers.VarargMatcher;
import org.mockito.invocation.Invocation;

public class MatcherApplicationStrategy {
Expand Down Expand Up @@ -63,7 +60,7 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
&& invocation.getRawArguments().length == matchers.size();

if (maybeVararg) {
final Class<?> matcherType = lastMatcher().type();
final Class<?> matcherType = lastMatcherType();
final Class<?> paramType = lastParameterType();
if (paramType.isAssignableFrom(matcherType)) {
return argsMatch(invocation.getRawArguments(), matchers, action);
Expand All @@ -74,12 +71,6 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
return argsMatch(invocation.getArguments(), matchers, action);
}

if (maybeVararg && isLastMatcherVarargMatcher()) {
int times = varargLength();
final List<? extends ArgumentMatcher<?>> matchers = appendLastMatcherNTimes(times);
return argsMatch(invocation.getArguments(), matchers, action);
}

return false;
}

Expand All @@ -98,33 +89,8 @@ private boolean argsMatch(
return true;
}

private boolean isLastMatcherVarargMatcher() {
ArgumentMatcher<?> argumentMatcher = lastMatcher();
if (argumentMatcher instanceof HamcrestArgumentMatcher<?>) {
return ((HamcrestArgumentMatcher<?>) argumentMatcher).isVarargMatcher();
}
return argumentMatcher instanceof VarargMatcher;
}

private List<? extends ArgumentMatcher<?>> appendLastMatcherNTimes(
int timesToAppendLastMatcher) {
ArgumentMatcher<?> lastMatcher = lastMatcher();

List<ArgumentMatcher<?>> expandedMatchers = new ArrayList<ArgumentMatcher<?>>(matchers);
for (int i = 0; i < timesToAppendLastMatcher; i++) {
expandedMatchers.add(lastMatcher);
}
return expandedMatchers;
}

private int varargLength() {
int rawArgumentCount = invocation.getRawArguments().length;
int expandedArgumentCount = invocation.getArguments().length;
return expandedArgumentCount - rawArgumentCount;
}

private ArgumentMatcher<?> lastMatcher() {
return matchers.get(matchers.size() - 1);
private Class<?> lastMatcherType() {
return matchers.get(matchers.size() - 1).type();
}

private Class<?> lastParameterType() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/mockito/internal/matchers/Any.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import org.mockito.ArgumentMatcher;

public class Any implements ArgumentMatcher<Object>, VarargMatcher, Serializable {
public class Any implements ArgumentMatcher<Object>, Serializable {

public static final Any ANY = new Any();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
import org.mockito.ArgumentMatcher;

@SuppressWarnings("unchecked")
public class CapturingMatcher<T>
implements ArgumentMatcher<T>, CapturesArguments, VarargMatcher, Serializable {
public class CapturingMatcher<T> implements ArgumentMatcher<T>, CapturesArguments, Serializable {

private final Class<? extends T> clazz;
private final List<Object> arguments = new ArrayList<>();
Expand Down
16 changes: 0 additions & 16 deletions src/main/java/org/mockito/internal/matchers/InstanceOf.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,4 @@ public Class<?> type() {
public String toString() {
return description;
}

public static class VarArgAware extends InstanceOf implements VarargMatcher {

public VarArgAware(Class<?> clazz) {
super(clazz);
}

public VarArgAware(Class<?> clazz, String describedAs) {
super(clazz, describedAs);
}

@Override
public Class<?> type() {
return clazz;
}
}
}
22 changes: 0 additions & 22 deletions src/main/java/org/mockito/internal/matchers/VarargMatcher.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,11 @@ public void should_capture_arguments_from_invocation() throws Exception {
}

@Test
public void should_match_varargs_using_any_varargs() throws Exception {
public void should_match_varargs_using_any_varargs() {
// given
mock.varargs("1", "2");
Invocation invocation = getLastInvocation();
InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(ANY));
InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, asList(ANY, ANY));

// when
boolean match = invocationMatcher.matches(invocation);
Expand All @@ -163,23 +163,23 @@ public void should_match_varargs_using_any_varargs() throws Exception {
}

@Test
public void should_capture_varargs_as_vararg() throws Exception {
public void should_capture_varargs_as_vararg() {
// given
mock.mixedVarargs(1, "a", "b");
Invocation invocation = getLastInvocation();
CapturingMatcher m = new CapturingMatcher(List.class);
CapturingMatcher<String[]> m = new CapturingMatcher(String[].class);
InvocationMatcher invocationMatcher =
new InvocationMatcher(invocation, Arrays.<ArgumentMatcher>asList(new Equals(1), m));

// when
invocationMatcher.captureArgumentsFrom(invocation);

// then
Assertions.assertThat(m.getAllValues()).containsExactly("a", "b");
Assertions.assertThat(m.getAllValues()).containsExactly(new String[] {"a", "b"});
}

@Test // like using several time the captor in the vararg
public void should_capture_arguments_when_args_count_does_NOT_match() throws Exception {
public void should_capture_arguments_when_args_count_does_NOT_match() {
// given
mock.varargs();
Invocation invocation = getLastInvocation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor;
import static org.mockito.internal.matchers.Any.ANY;

Expand All @@ -26,7 +25,6 @@
import org.mockito.internal.matchers.Any;
import org.mockito.internal.matchers.Equals;
import org.mockito.internal.matchers.InstanceOf;
import org.mockito.internal.matchers.VarargMatcher;
import org.mockito.invocation.Invocation;
import org.mockitousage.IMethods;
import org.mockitoutil.TestBase;
Expand Down Expand Up @@ -124,7 +122,7 @@ public void shouldKnowWhenVarargsMatch() {
public void shouldAllowAnyMatchEntireVararg() {
// given
invocation = varargs("1", "2");
matchers = asList(ANY);
matchers = asList(ANY, ANY);

// when
boolean match =
Expand All @@ -151,10 +149,10 @@ public void shouldNotAllowAnyWithMixedVarargs() {
}

@Test
public void shouldAllowanyWithMixedVarargs() {
public void shouldAllowAnyWithMixedVarargs() {
// given
invocation = mixedVarargs(1, "1", "2");
matchers = asList(new Equals(1), ANY);
matchers = asList(new Equals(1), ANY, ANY);

// when
boolean match =
Expand Down Expand Up @@ -186,7 +184,7 @@ public void shouldAnyDealWithDifferentSizeOfArgs() {
public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() {
// given
invocation = mixedVarargs(null, null, "2");
matchers = asList(new Equals(null), ANY);
matchers = asList(new Equals(null), ANY, ANY);

// when
getMatcherApplicationStrategyFor(invocation, matchers)
Expand All @@ -200,7 +198,7 @@ public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() {
public void shouldMatchAnyEvenIfMatcherIsDecorated() {
// given
invocation = varargs("1", "2");
matchers = asList(ANY);
matchers = asList(ANY, ANY);

// when
getMatcherApplicationStrategyFor(invocation, matchers)
Expand All @@ -216,7 +214,7 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() {
invocation = varargs("1", "2");
HamcrestArgumentMatcher<Integer> argumentMatcher =
new HamcrestArgumentMatcher<>(new IntMatcher());
matchers = asList(argumentMatcher);
matchers = asList(argumentMatcher, argumentMatcher);

// when
getMatcherApplicationStrategyFor(invocation, matchers)
Expand All @@ -230,7 +228,7 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() {
public void shouldMatchAnyThatMatchesRawVarArgType() {
// given
invocation = varargs("1", "2");
InstanceOf.VarArgAware any = new InstanceOf.VarArgAware(String[].class, "<any String[]>");
InstanceOf any = new InstanceOf(String[].class, "<any String[]>");
matchers = asList(any);

// when
Expand All @@ -241,7 +239,15 @@ public void shouldMatchAnyThatMatchesRawVarArgType() {
recordAction.assertContainsExactly(any);
}

private static class IntMatcher extends BaseMatcher<Integer> implements VarargMatcher {
private static class IntMatcher extends BaseMatcher<Integer> {
public boolean matches(Object o) {
return true;
}

public void describeTo(Description description) {}
}

private static class IntArrayMatcher extends BaseMatcher<Integer[]> {
public boolean matches(Object o) {
return true;
}
Expand Down