Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package nl.tudelft.cse1110.andy.codechecker.checks;

import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.*;


/**
Expand Down Expand Up @@ -44,7 +42,7 @@ public boolean visit(MethodInvocation mi) {
@Override
public boolean visit(FieldDeclaration fd) {
// added this if-check to avoid overriding classWasMocked erroneously.
if(!classWasMocked) {
if (!classWasMocked) {
boolean hasMockAnnotation = fd.modifiers().stream()
.anyMatch(m -> m instanceof Annotation &&
((Annotation) m).getTypeName().getFullyQualifiedName().equals("Mock"));
Expand All @@ -59,6 +57,53 @@ public boolean visit(FieldDeclaration fd) {
return super.visit(fd);
}

/**
* Detects this pattern: arrayListT = mock();
* i.e. class attribute on the left, and parameterless mock() invocation on the right
*/
@Override
public boolean visit(Assignment a) {
if (classWasMocked) {
return super.visit(a);
}

if (a.getLeftHandSide() instanceof SimpleName s) {
var binding = s.resolveTypeBinding();
if (binding != null && binding.getName().equals(classToBeMocked)) {
var right = a.getRightHandSide();
classWasMocked = isParameterlessMockMethodInvocation(right);
}
}

return super.visit(a);
}

/**
* Detects this pattern: SortedMap<String, Integer> sortedMap = mock();
* i.e. variable with type on the left, and parameterless mock() invocation on the right
*/
@Override
public boolean visit(VariableDeclarationFragment fragment) {
if (!classWasMocked) {
String className = fragment.resolveBinding().getType().getBinaryName();
String simpleName = className.substring(className.lastIndexOf('.') + 1);
if (simpleName.equals(classToBeMocked)) {
var initializer = fragment.getInitializer();
classWasMocked = isParameterlessMockMethodInvocation(initializer);
}
}
return super.visit(fragment);
}

private boolean isParameterlessMockMethodInvocation(Expression expr) {
if (expr instanceof MethodInvocation mi) {
boolean mockMethodCalled = "mock".equals(mi.getName().toString());
boolean noParams = mi.arguments() != null && mi.arguments().isEmpty();
return mockMethodCalled && noParams;
}

return false;
}

@Override
public boolean result() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import static org.assertj.core.api.Assertions.assertThat;

public class MockClassTest extends ChecksBaseTest {

@ParameterizedTest
@CsvSource({"List,true", "Set,true", "Queue,true", "Deque,true", "HashMap,false"})
@CsvSource({"List,true", "Set,true", "ArrayList,true", "SortedMap, true", "Queue,true", "Deque,true", "HashMap,false"})
void findMocks(String classToMock, boolean expectation) {
Check check = new MockClass(classToMock);
run("ManyMocks.java", check);
assertThat(check.result()).isEqualTo(expectation);
}

}
8 changes: 8 additions & 0 deletions andy/src/test/resources/codechecker/fixtures/ManyMocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Queue;
import java.util.Deque;
import java.util.SortedSet;
import java.util.SortedMap;

import static org.mockito.Mockito.mock;
import org.mockito.MockitoAnnotations;
Expand Down Expand Up @@ -36,6 +38,8 @@ public class ManyMocks {
@Mock
Deque<String> mockedDQ;

ArrayList arrayListT;

@BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
Expand All @@ -47,6 +51,10 @@ void t1() {
List<String> mockedList = Mockito.mock(List.class);
// with the static import
Set<String> mockedSet = mock(Set.class);
// without specifying the class name (deriving from the type of the variable)
SortedMap<String, Integer> sortedMap = mock();
// without specifying the class name (deriving from the type of the variable), without an explicit type on the left
arrayListT = mock();
// no mock
HashMap<String, String> concreteHashMap = new HashMap<>();
}
Expand Down