Skip to content

Commit

Permalink
Add more filters, refine displayname
Browse files Browse the repository at this point in the history
  • Loading branch information
Deee92 committed Jul 10, 2022
1 parent 1b0eee6 commit f6345cd
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -524,14 +524,14 @@ public void generateMockMethods(int testMethodCounter,
// Test - OO
if (!instrumentedMethod.getReturnType().equals("void") &
MockGeneratorUtil.arePrimitiveOrString(List.of(instrumentedMethod.getReturnType()))) {
CtMethod<?> testOO = mockGenerator.generateTestByCategory(testMethodCounter, "OO", mockMethod, serializedObject, generatedClass, instrumentedMethod);
CtMethod<?> testOO = mockGenerator.generateTestByCategory(testMethodCounter, MockOracle.OO, mockMethod, serializedObject, generatedClass, instrumentedMethod);
generatedClass.addMethod(testOO);
generatedTestsWithMocks.add(testOO);
System.out.println("Generated 1 OO test for " + instrumentedMethod.getFullMethodPath());
}

// Test - PO
CtMethod<?> testPO = mockGenerator.generateTestByCategory(testMethodCounter, "PO", mockMethod, serializedObject, generatedClass, instrumentedMethod);
CtMethod<?> testPO = mockGenerator.generateTestByCategory(testMethodCounter, MockOracle.PO, mockMethod, serializedObject, generatedClass, instrumentedMethod);
generatedClass.addMethod(testPO);
generatedTestsWithMocks.add(testPO);
System.out.println("Generated 1 PO test for " + instrumentedMethod.getFullMethodPath());
Expand All @@ -552,7 +552,7 @@ public void generateMockMethods(int testMethodCounter,
CtMethod<?> testCO = sequenceGenerator.generateTestToVerifyMethodSequence(Set.of(testPO), serializedObject);
testCO.setSimpleName(methodNameCO);
testCO.addAnnotation(testAnnotation);
testCO.addAnnotation(MockGeneratorUtil.generateDisplayName(testMethodCounter, "CO", instrumentedMethod.getMethodName(),
testCO.addAnnotation(MockGeneratorUtil.generateDisplayName(testMethodCounter, MockOracle.CO, instrumentedMethod.getMethodName(),
instrumentedMethod.getNestedInvocations().stream().map(NestedInvocation::getInvocation).collect(Collectors.toList()).toString()));
generatedClass.addMethod(testCO);
generatedTestsWithMocks.add(testCO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public static void onReturn(@BindReturn Object returnedObject,
@BindTraveler TraceEntry traceEntry) {
if (fileSizeWithinLimits) {
writeObjectXMLToFile(returnedObject, returnedObjectFilePath);
writeObjectProfileSizeToFile(getObjectProfileSize() - profileSizePre);
// writeObjectProfileSizeToFile(getObjectProfileSize() - profileSizePre);
checkFileSizeLimit();
}
INVOCATION_COUNT++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private CtStatementList prepareWhenThenInvocation(NestedInvocation nestedInvocat
* @throws ClassNotFoundException
*/
public CtMethod<?> generateTestByCategory(int testMethodCounter,
String testCategory,
MockOracle testCategory,
CtMethod<?> baseMethod,
SerializedObject serializedObjectMUT,
CtClass<?> generatedTestClass,
Expand Down Expand Up @@ -209,7 +209,7 @@ public CtMethod<?> generateTestByCategory(int testMethodCounter,
}

// Generate verification statements for PO and CO
if (!testCategory.equals("OO")) {
if (!testCategory.equals(MockOracle.OO)) {
CtStatement verificationStatement = createMockitoVerifyInvocation(nestedInvocation,
mockVariableName, paramTypes, nestedParams, nestedSerializedObject);
if (verificationStatementsPO.stream().noneMatch(v ->
Expand Down Expand Up @@ -256,22 +256,22 @@ public CtMethod<?> generateTestByCategory(int testMethodCounter,
targetMUT.getParamList(), mockedParamIndices.get(i), mockParameterNames.get(i));
}

if (testCategory.equals("OO")) {
if (testCategory.equals(MockOracle.OO)) {
List<CtStatement> actAndAssertStatements = MockGeneratorUtil.refactorAssertionStatementIntoActAndAssertion(
targetMUT.getReturnType(), mutCallStatement);
actAndAssertStatements.forEach(s -> generatedTest.getBody().addStatement(s));
}

// remove assertion on MUT output for PO and CO tests
if (!testCategory.equals("OO")) {
if (!testCategory.equals(MockOracle.OO)) {
mutCallStatement = factory.createCodeSnippetStatement(mutCallStatement.toString()
.replaceAll(".+\\(.+,\\s(receivingObject.+\\))\\)",
"$1"));
generatedTest.getBody().addStatement(mutCallStatement);
}

// For PO tests, add verification statements with concrete parameters
if (testCategory.equals("PO")) {
if (testCategory.equals(MockOracle.PO)) {
for (CtStatement statement : verificationStatementsPO) {
generatedTest.getBody().addStatement(statement);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package se.kth.castor.pankti.generate.generators;

public enum MockOracle {
OO,
PO,
CO;

public String getFullName(MockOracle oracleEnum) {
String oracleName;
if (oracleEnum.toString().equals("OO"))
oracleName = "output";
else if (oracleEnum.toString().equals("PO"))
oracleName = "parameter";
else oracleName = "call";
return oracleName + " oracle";
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package se.kth.castor.pankti.generate.util;

import se.kth.castor.pankti.generate.data.SerializedObject;
import se.kth.castor.pankti.generate.generators.MockOracle;
import spoon.reflect.code.*;
import spoon.reflect.declaration.*;
import spoon.reflect.factory.Factory;
Expand Down Expand Up @@ -590,14 +591,14 @@ public static String getNestedParamsArgumentForInvocations(List<String> paramTyp
}

public static CtAnnotation<?> generateDisplayName(int testMethodCounter,
String category, String mutName,
MockOracle category, String mutName,
String mockables)
throws ClassNotFoundException {
CtAnnotation<?> displayNameAnnotation = factory.createAnnotation(
factory.createCtTypeReference(Class.forName(JUNIT_JUPITER_DISPLAYNAME_REFERENCE)));
CtLiteral<String> annotationLiteral = factory.createLiteral();
annotationLiteral.setValue(String.format("%s-%s with %s, mocking %s",
mutName, testMethodCounter, category, mockables));
mutName, testMethodCounter, category.getFullName(category), mockables));
displayNameAnnotation.addValue("value", annotationLiteral);
return displayNameAnnotation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.reflect.reference.CtExecutableReferenceImpl;
import spoon.support.reflect.reference.CtFieldReferenceImpl;

import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -180,12 +180,27 @@ public static List<CtInvocation<?>> findNestedMethodCallsOnFields(final CtMethod
.collect(Collectors.toList());
}

private static boolean isExecutableEqualsOrHashCodeOrToString(CtExecutableReference<?> executable) {
private static boolean isExecutableNonInstrumentableOrNonMockable(CtExecutableReference<?> executable) {
return (executable.getSimpleName().equals("equals") ||
executable.getSimpleName().equals("toString") ||
(executable.getSimpleName().equals("containsKey") &
getDeclaringType(executable).getQualifiedName().equals("java.util.Map")) ||
executable.getSimpleName().equals("hashCode"));
executable.getSimpleName().equals("hashCode") ||
(executable.getSimpleName().equals("accept") &
getDeclaringType(executable).getQualifiedName().equals("java.util.function.Consumer")) ||
((executable.getSimpleName().equals("contains") ||
executable.getSimpleName().equals("add")) &
(getDeclaringType(executable).getQualifiedName().equals("java.util.Set") ||
(getDeclaringType(executable).getQualifiedName().equals("java.util.HashSet")))) ||
((executable.getSimpleName().equals("isEmpty") ||
executable.getSimpleName().equals("size")) &
getDeclaringType(executable).getQualifiedName().equals("java.util.List")) ||
(executable.getSimpleName().toLowerCase(Locale.ROOT).contains("lock") || getDeclaringType(executable).getQualifiedName().contains("lock")) ||
((executable.getSimpleName().equals("containsKey") ||
executable.getSimpleName().equals("put") ||
executable.getSimpleName().equals("putAll") ||
executable.getSimpleName().equals("get") ||
executable.getSimpleName().equals("remove")) &
(getDeclaringType(executable).getQualifiedName().equals("java.util.Map") ||
getDeclaringType(executable).getQualifiedName().equals("java.util.HashMap"))));
}

/**
Expand Down Expand Up @@ -245,7 +260,10 @@ public static List<CtInvocation<?>> getMockableInvocationsOnParameters(final CtM
CtExecutableReference<?> executable = getExecutable(invocation);
if (!isExecutableDeclaringTypeStringOrCollection(getDeclaringType(executable))
& executable.getType() != null) {
if (!isExecutableEqualsOrHashCodeOrToString(executable) &
if (!isExecutableNonInstrumentableOrNonMockable(executable) &
!isMUTParentAnonymousOrPrivate(method) &
!isMockableParentPrivateOrStatic(invocation) &
!isMockablePrivate(invocation) &
isPrimitiveOrString(executable.getType()) & getExecutableLOC(executable) != 1) {
invocationsOnParams.add(invocation);
}
Expand Down Expand Up @@ -310,16 +328,43 @@ private static boolean areNestedInvocationParamsPrimitive(CtInvocation<?> invoca
return invocation.getExecutable().getParameters().stream().allMatch(CtTypeInformation::isPrimitive);
}

/**
* Check if a method is defined within an anonymous or private class
*/
private static boolean isMUTParentAnonymousOrPrivate(CtMethod<?> method) {
CtType<?> parentType = method.getDeclaringType();
return parentType.getModifiers().contains(ModifierKind.PRIVATE) ||
parentType.isAnonymous();
}

private static boolean isMockableParentPrivateOrStatic(CtInvocation<?> invocation) {
CtTypeReference<?> mockableDeclaringType = getDeclaringType(getExecutable(invocation));
Set<ModifierKind> modifiers = mockableDeclaringType.getModifiers();
return modifiers.contains(ModifierKind.STATIC) || modifiers.contains(ModifierKind.PRIVATE);
}

private static boolean isMockablePrivate(CtInvocation<?> invocation) {
try {
int modifiers = getExecutable(invocation).getActualMethod().getModifiers();
return Modifier.toString(modifiers).contains("private");
} catch (Exception e) {
return false;
}
}

private static boolean isNestedInvocationOnFieldMockable(final CtMethod<?> method,
final CtInvocation<?> invocation) {
if (isMUTParentAnonymousOrPrivate(method) || isMockableParentPrivateOrStatic(invocation) ||
isMockablePrivate(invocation))
return false;
CtExecutableReference<?> executable = getExecutable(invocation);
CtTypeReference<?> executableDeclaringType = getDeclaringType(executable);
if (executableDeclaringType == null || executable.getType() == null)
return false;
if (isExecutableDeclaringTypeStringOrCollection(executableDeclaringType))
return false;
CtType<?> methodDeclaringType = method.getDeclaringType();
if (!isExecutableEqualsOrHashCodeOrToString(executable) &
if (!isExecutableNonInstrumentableOrNonMockable(executable) &
!isMethodDeclaringTypeSameAsExecutableDeclaringType(methodDeclaringType, executableDeclaringType)) {
return isInvocationTargetAField(method, invocation);
}
Expand All @@ -333,7 +378,7 @@ private static boolean isInvocationTargetAnExternalParameter(final CtMethod<?> m
if (invocation.getTarget() != null) {
if (invocation.getTarget().toString().equals(parameter.getSimpleName()) &
!parameter.getType().getQualifiedName().equals(method.getDeclaringType().getQualifiedName()) &
!isExecutableEqualsOrHashCodeOrToString(invocation.getExecutable()))
!isExecutableNonInstrumentableOrNonMockable(invocation.getExecutable()))
return true;
}
}
Expand All @@ -352,7 +397,10 @@ private static String getFieldModifier(CtMethod<?> method,
.getField(target)
.getModifiers();

return modifiers.contains(ModifierKind.PRIVATE) ?
// Mocks for private, protected, and final fields are injected through reflection
return (modifiers.contains(ModifierKind.PRIVATE) ||
modifiers.contains(ModifierKind.PROTECTED) ||
modifiers.contains(ModifierKind.FINAL)) ?
ModifierKind.PRIVATE.toString() :
"default";
}
Expand Down

0 comments on commit f6345cd

Please sign in to comment.