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

Fixes mockito#2311 #2320

Merged
merged 4 commits into from Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -9,18 +9,25 @@
* When ArgumentMatcher fails, chance is that the actual object has the same output of toString() than
* the wanted object. This looks weird when failures are reported.
* Therefore when matcher fails but toString() yields the same outputs,
* we will try to use the {@link #toStringWithType()} method.
* we will try to use the {@link #toStringWithType(boolean)} method.
*/
public interface ContainsExtraTypeInfo {

/**
* @param useFullyQualifiedClassName - uses fully qualified class name if true else simple class name
* Returns more verbose description of the object which include type information
*/
String toStringWithType();
String toStringWithType(boolean useFullyQualifiedClassName);

/**
* Checks if target target has matching type.
* If the type matches, there is no point in rendering result from {@link #toStringWithType()}
* If the type matches, there is no point in rendering result from {@link #toStringWithType(boolean)}
*/
boolean typeMatches(Object target);

/**
*
* @return Returns the Class of the argument
*/
Class getWantedClass();
}
10 changes: 8 additions & 2 deletions src/main/java/org/mockito/internal/matchers/Equals.java
Expand Up @@ -51,12 +51,18 @@ public int hashCode() {
}

@Override
public String toStringWithType() {
return "(" + wanted.getClass().getSimpleName() + ") " + describe(wanted);
public String toStringWithType(boolean useFullyQualifiedClassName) {
saurabh7248 marked this conversation as resolved.
Show resolved Hide resolved
return "(" + (useFullyQualifiedClassName ? wanted.getClass().getCanonicalName() : wanted.getClass().getSimpleName()) + ") " + describe(wanted);
}

@Override
public boolean typeMatches(Object target) {
return wanted != null && target != null && target.getClass() == wanted.getClass();
}

@Override
public Class getWantedClass() {
saurabh7248 marked this conversation as resolved.
Show resolved Hide resolved
return wanted.getClass();
}

}
Expand Up @@ -31,7 +31,10 @@ private Iterator<FormattedText> applyPrintSettings(
int i = 0;
for (final ArgumentMatcher matcher : matchers) {
if (matcher instanceof ContainsExtraTypeInfo && printSettings.extraTypeInfoFor(i)) {
out.add(new FormattedText(((ContainsExtraTypeInfo) matcher).toStringWithType()));
out.add(new FormattedText(((ContainsExtraTypeInfo) matcher).toStringWithType(false)));
} else if(matcher instanceof ContainsExtraTypeInfo
&& printSettings.fullyQualifiedNameFor(((ContainsExtraTypeInfo) matcher).getWantedClass().getSimpleName())){
out.add(new FormattedText(((ContainsExtraTypeInfo) matcher).toStringWithType(true)));
saurabh7248 marked this conversation as resolved.
Show resolved Hide resolved
} else {
out.add(new FormattedText(MatcherToString.toString(matcher)));
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/mockito/internal/reporting/PrintSettings.java
Expand Up @@ -5,8 +5,10 @@
package org.mockito.internal.reporting;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.text.MatchersPrinter;
Expand All @@ -19,6 +21,7 @@ public class PrintSettings {
public static final int MAX_LINE_LENGTH = 45;
private boolean multiline;
private List<Integer> withTypeInfo = new LinkedList<>();
private Set<String> withFullyQualifiedName = Collections.emptySet();

public void setMultiline(boolean multiline) {
this.multiline = multiline;
Expand All @@ -38,10 +41,18 @@ public boolean extraTypeInfoFor(int argumentIndex) {
return withTypeInfo.contains(argumentIndex);
}

public boolean fullyQualifiedNameFor(String simpleClassName) {
return withFullyQualifiedName.contains(simpleClassName);
}

public void setMatchersToBeDescribedWithExtraTypeInfo(Integer[] indexesOfMatchers) {
this.withTypeInfo = Arrays.asList(indexesOfMatchers);
}

public void setMatchersToBeDescribedWithFullName(Set<String> indexesOfMatchers) {
this.withFullyQualifiedName= indexesOfMatchers;
}

public String print(List<ArgumentMatcher> matchers, Invocation invocation) {
MatchersPrinter matchersPrinter = new MatchersPrinter();
String qualifiedName =
Expand Down
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.mockito.invocation.Invocation;
import org.mockito.invocation.MatchableInvocation;
Expand All @@ -28,17 +29,21 @@ public SmartPrinter(
this(
wanted,
Collections.singletonList(actual),
indexesOfMatchersToBeDescribedWithExtraTypeInfo);
indexesOfMatchersToBeDescribedWithExtraTypeInfo,
Collections.emptySet());
}

public SmartPrinter(
MatchableInvocation wanted,
List<Invocation> allActualInvocations,
Integer... indexesOfMatchersToBeDescribedWithExtraTypeInfo) {
Integer[] indexesOfMatchersToBeDescribedWithExtraTypeInfo,
Set<String> classNamesToBeDescribedWithFullName) {
PrintSettings printSettings = new PrintSettings();
printSettings.setMultiline(isMultiLine(wanted, allActualInvocations));
printSettings.setMatchersToBeDescribedWithExtraTypeInfo(
indexesOfMatchersToBeDescribedWithExtraTypeInfo);
printSettings.setMatchersToBeDescribedWithFullName(
classNamesToBeDescribedWithFullName);

this.wanted = printSettings.print(wanted);

Expand Down
Expand Up @@ -4,8 +4,13 @@
*/
package org.mockito.internal.verification.argumentmatching;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.ContainsExtraTypeInfo;
Expand Down Expand Up @@ -48,4 +53,30 @@ private static boolean safelyMatches(ArgumentMatcher m, Object arg) {
private static boolean toStringEquals(ArgumentMatcher m, Object arg) {
return m.toString().equals(String.valueOf(arg));
}

/**
* Suspiciously not matching arguments are those that don't match, and the classes have same simple name.
*/
public static Set<String> getNotMatchingArgsWithSameName(
List<ArgumentMatcher> matchers, Object[] arguments) {
Set<String> repeatedClassNames = new HashSet<>();
Map<String, Set<String>> classesHavingSameName = new HashMap<>();
for (ArgumentMatcher m : matchers) {
if (m instanceof ContainsExtraTypeInfo) {
Class wantedClass = ((ContainsExtraTypeInfo) m).getWantedClass();
classesHavingSameName.computeIfAbsent(wantedClass.getSimpleName(), className -> new HashSet<>())
.add(wantedClass.getCanonicalName());
}
}
for (Object argument : arguments) {
Class wantedClass = argument.getClass();
classesHavingSameName.computeIfAbsent(wantedClass.getSimpleName(), className -> new HashSet<>())
.add(wantedClass.getCanonicalName());
}
return classesHavingSameName.entrySet().stream()
.filter(classEntry -> classEntry.getValue().size() > 1)
.map(classEntry -> classEntry.getKey())
.collect(Collectors.toSet());
}

}
Expand Up @@ -11,9 +11,11 @@
import static org.mockito.internal.invocation.InvocationsFinder.findInvocations;
import static org.mockito.internal.invocation.InvocationsFinder.findPreviousVerifiedInOrder;
import static org.mockito.internal.invocation.InvocationsFinder.findSimilarInvocation;
import static org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool.getNotMatchingArgsWithSameName;
import static org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool.getSuspiciouslyNotMatchingArgsIndexes;

import java.util.List;
import java.util.Set;

import org.mockito.internal.reporting.SmartPrinter;
import org.mockito.internal.util.collections.ListUtil;
Expand Down Expand Up @@ -41,7 +43,9 @@ public static void checkMissingInvocation(

Integer[] indexesOfSuspiciousArgs =
getSuspiciouslyNotMatchingArgsIndexes(wanted.getMatchers(), similar.getArguments());
SmartPrinter smartPrinter = new SmartPrinter(wanted, invocations, indexesOfSuspiciousArgs);
Set<String> classesWithSameSimpleName =
getNotMatchingArgsWithSameName(wanted.getMatchers(), similar.getArguments());
SmartPrinter smartPrinter = new SmartPrinter(wanted, invocations, indexesOfSuspiciousArgs,classesWithSameSimpleName);
List<Location> actualLocations =
ListUtil.convert(
invocations,
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/org/mockito/internal/matchers/EqualsTest.java
Expand Up @@ -28,21 +28,21 @@ public void shouldArraysBeEqual() {

@Test
public void shouldDescribeWithExtraTypeInfo() throws Exception {
String descStr = new Equals(100).toStringWithType();
String descStr = new Equals(100).toStringWithType(false);

assertEquals("(Integer) 100", descStr);
}

@Test
public void shouldDescribeWithExtraTypeInfoOfLong() throws Exception {
String descStr = new Equals(100L).toStringWithType();
String descStr = new Equals(100L).toStringWithType(false);

assertEquals("(Long) 100L", descStr);
}

@Test
public void shouldDescribeWithTypeOfString() throws Exception {
String descStr = new Equals("x").toStringWithType();
String descStr = new Equals("x").toStringWithType(false);

assertEquals("(String) \"x\"", descStr);
}
Expand Down
Expand Up @@ -111,14 +111,20 @@ public boolean matches(String item) {
}

@Override
public String toStringWithType() {
public String toStringWithType(boolean useFullyQualifiedClassName) {
return "";
}

@Override
public boolean typeMatches(Object target) {
return true;
}

@Override
public Class getWantedClass() {
return String.class;
}

}

// given
Expand Down