Skip to content

Commit

Permalink
Fix is collection and is map in observations (#771)
Browse files Browse the repository at this point in the history
Fix: is collection and is map in observations
  • Loading branch information
danglotb authored Apr 24, 2019
1 parent 0c18865 commit 0a3c67b
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class MethodsHandler {
forbiddenClasses.add("java.util.Date");
forbiddenClasses.add("java.net.URL");
forbiddenClasses.add("java.util.Calendar");
forbiddenClasses.add("java.io.File");

forbiddenPackages = new ArrayList<>();
forbiddenPackages.add("java.time");
Expand Down
8 changes: 2 additions & 6 deletions dspot/src/main/java/eu/stamp_project/compare/ObjectLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ public static void reset() {
}

public static void log(Object objectToObserve, String objectObservedAsString, String id) {
/*if (objectToObserve == null) {
getSingleton().addObservation(id, "null", null);
return;
}*/
getSingleton()._log(
objectToObserve,
objectToObserve,
Expand All @@ -77,13 +73,13 @@ private void _log(Object startingObject,
if (deep <= maxDeep) {
final boolean primitive = Utils.isPrimitive(objectToObserve);
final boolean primitiveArray = Utils.isPrimitiveArray(objectToObserve);
final boolean primitiveCollectionOrMap = Utils.isPrimitiveCollectionOrMap(objectToObserve);
final boolean primitiveCollectionOrMap = Utils.isNonEmptyPrimitiveCollectionOrMap(objectToObserve);
if (objectToObserve == null) {
addObservation(id, observedObjectAsString, null);
} else if (isSerializable(objectToObserve) &&
(primitive || primitiveArray || primitiveCollectionOrMap)) {
addObservation(id, observedObjectAsString, objectToObserve);
} else if (Utils.isCollection(objectToObserve)) {
} else if (Utils.isCollection(objectToObserve)) { // the object is empty here
addObservation(id, observedObjectAsString + ".isEmpty()", ((Collection) objectToObserve).isEmpty());
} else if (Utils.isMap(objectToObserve)) {
addObservation(id, observedObjectAsString + ".isEmpty()", ((Map) objectToObserve).isEmpty());
Expand Down
2 changes: 1 addition & 1 deletion dspot/src/main/java/eu/stamp_project/compare/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Utils {

protected static Set<Class<?>> WRAPPER_TYPES = getWrapperTypes();

public static boolean isPrimitiveCollectionOrMap(Object collectionOrMap) {
public static boolean isNonEmptyPrimitiveCollectionOrMap(Object collectionOrMap) {
try {
return collectionOrMap != null &&
(isNonEmptyPrimitiveCollection(collectionOrMap) || isNonEmptyPrimitiveMap(collectionOrMap));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtVariableReference;

import java.util.HashSet;
import java.util.Set;
import java.util.Collection;
import java.util.Map;

/**
* Created by Benjamin DANGLOT
Expand Down Expand Up @@ -60,9 +60,12 @@ public CtExpression<?> buildInvocationFromString(String invocationAsString) {
final CtExpression<?> invocation = this.buildInvocationFromString(invocationAsString, null);
if (invocation instanceof CtInvocation<?> &&
"isEmpty".equals(((CtInvocation) invocation).getExecutable().getSimpleName())) {
// TODO check if this block is used
addTypeCastToCollectionIfNeeded(invocation);
return invocation;
}
if (invocationAsString.endsWith("isEmpty()")) {
addTypeCastToCollectionIfNeeded(invocation);
final CtType<?> listCtType = factory.Type()
.get(java.util.List.class);
final CtMethod<?> isEmpty = listCtType.getMethodsByName("isEmpty").get(0);
Expand All @@ -75,6 +78,23 @@ public CtExpression<?> buildInvocationFromString(String invocationAsString) {
}
}

private void addTypeCastToCollectionIfNeeded(CtExpression<?> invocation) {
try {
if (invocation instanceof CtInvocation<?>) {
final Class<?> actualClass = ((CtInvocation<?>) invocation).getExecutable()
.getDeclaration()
.getType()
.getActualClass();
if (!actualClass.equals(Collection.class) && !actualClass.isAssignableFrom(Map.class) ) {
invocation.addTypeCast(invocation.getFactory().createCtTypeReference(Collection.class));
}
}
} catch (Exception ignored) {
// we force the cast here, but the test will be probably uncompilable.
invocation.addTypeCast(invocation.getFactory().createCtTypeReference(Collection.class));
}
}

private CtExpression<?> buildInvocationFromString(String invocationAsString, CtInvocation<?> subInvocation) {
CtInvocation invocation = factory.createInvocation();
int end = invocationAsString.indexOf("()");
Expand Down Expand Up @@ -119,23 +139,6 @@ private CtExpression<?> buildInvocationFromString(String invocationAsString, CtI
}
}

// TODO this a hotfix, and must be reworked. The issue has been reported in Spoon see: https://github.com/INRIA/spoon/issues/2378
@Deprecated
private Set<CtMethod<?>> getAllMethods(CtType<?> type) {
try {
return type.getAllMethods();
} catch (StackOverflowError e) {
LOGGER.error("Overflow!");
final Set<CtMethod<?>> methods = new HashSet<>(type.getMethods());
CtTypeReference<?> currentSuperClass = type.getSuperclass();
while (currentSuperClass != null) {
methods.addAll(currentSuperClass.getTypeDeclaration().getMethods());
currentSuperClass = currentSuperClass.getSuperclass();
}
return methods;
}
}

private CtExpression<?> buildTargetFromString(String invocationAsString) {
final CtLocalVariableReference<?> localVariableReference = factory.createLocalVariableReference();
localVariableReference.setSimpleName(invocationAsString.split("\\.")[0]);
Expand Down
Binary file modified dspot/src/main/resources/compare/MethodsHandler.class
Binary file not shown.
Binary file modified dspot/src/main/resources/compare/ObjectLog.class
Binary file not shown.
Binary file modified dspot/src/main/resources/compare/Utils.class
Binary file not shown.
13 changes: 8 additions & 5 deletions dspot/src/test/java/eu/stamp_project/compare/ObjectLogTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import org.junit.Test;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
Expand Down Expand Up @@ -63,6 +60,12 @@ public int random() {
public String getAbsolutePath() {
return new File(".").getAbsolutePath();
}
public Iterable<String> getIterable() {
return Collections.singleton("");
}
public Collection<String> getCollection() {
return Collections.singleton("");
}
}

public static Integer add(Integer a, Integer b) {
Expand All @@ -84,7 +87,7 @@ public void test() throws Exception {
assertNotNull(add__0);
// assertEquals(1, add__0.getNotDeterministValues().size());
final Map<String, Object> observationValues = add__0.getObservationValues();
assertEquals(3, observationValues.size());
assertEquals(5, observationValues.size());
// assertEquals(25, observationValues.get("(myInternalClass ).compute()"));
assertEquals(3, observationValues.get("(myInternalClass ).getA()"));
assertEquals(20, observationValues.get("(myInternalClass ).getB()"));
Expand Down
19 changes: 9 additions & 10 deletions dspot/src/test/java/eu/stamp_project/compare/UtilsTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package eu.stamp_project.compare;

import org.junit.Ignore;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.*;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
Expand All @@ -19,21 +18,21 @@ public class UtilsTest {
@Test//TODO empty collection / map are considered as primitive, we may need to found a semantic of the method.
public void testIsPrimitiveCollectionOrMap() throws Exception {
final ArrayList<Integer> list = new ArrayList<>();
assertFalse(Utils.isPrimitiveCollectionOrMap(list));
assertFalse(Utils.isNonEmptyPrimitiveCollectionOrMap(list));
list.add(1);
assertTrue(Utils.isPrimitiveCollectionOrMap(list));
assertTrue(Utils.isNonEmptyPrimitiveCollectionOrMap(list));

final HashMap<Integer, Integer> map = new HashMap<>();
assertFalse(Utils.isPrimitiveCollectionOrMap(map));
assertFalse(Utils.isNonEmptyPrimitiveCollectionOrMap(map));
map.put(1,1);
assertTrue(Utils.isPrimitiveCollectionOrMap(map));
assertTrue(Utils.isNonEmptyPrimitiveCollectionOrMap(map));

final HashSet<Integer> set = new HashSet<>();
assertFalse(Utils.isPrimitiveCollectionOrMap(set));
assertFalse(Utils.isNonEmptyPrimitiveCollectionOrMap(set));
set.add(1);
assertTrue(Utils.isPrimitiveCollectionOrMap(set));
assertTrue(Utils.isNonEmptyPrimitiveCollectionOrMap(set));

assertFalse(Utils.isPrimitiveCollectionOrMap(1));
assertFalse(Utils.isNonEmptyPrimitiveCollectionOrMap(1));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import eu.stamp_project.Utils;
import eu.stamp_project.test_framework.TestFramework;
import eu.stamp_project.utils.AmplificationHelper;
import eu.stamp_project.utils.DSpotUtils;
import eu.stamp_project.utils.program.InputConfiguration;
import eu.stamp_project.utils.program.InputConfigurationProperty;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -19,11 +23,10 @@

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;

/**
* Created by Benjamin DANGLOT
Expand All @@ -32,6 +35,7 @@
*/
public class AssertGeneratorTest extends AbstractTest {

public static final String THE_METHOD_IS_EMPTY_IS_UNDEFINED_FOR_THE_TYPE = "The method isEmpty() is undefined for the type ";
private AssertGenerator assertGenerator;

@Override
Expand Down Expand Up @@ -69,8 +73,8 @@ public void testOnInfiniteLoop() throws Exception {

private static final String ASSERT_EQUALS = "assertEquals";
private static final String ASSERT_TRUE = "assertTrue";
private static final String ASSERT_FALSE= "assertFalse";
private static final String ASSERT_NULL= "assertNull";
private static final String ASSERT_FALSE = "assertFalse";
private static final String ASSERT_NULL = "assertNull";

/**
* This class is used to verify that the method, with the given name, is inside an assertion
Expand Down Expand Up @@ -103,7 +107,7 @@ public boolean matches(CtInvocation<?> element) {
}

@Test
public void testOnFieldRead() throws Exception {
public void testOnFieldRead() {

/*
Test that we can generate as expected variable in assertion field read such as DOUBLE.NEGATIVE_INFINITY
Expand All @@ -119,12 +123,11 @@ public void testOnFieldRead() throws Exception {
assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getInfinity", ASSERT_EQUALS)).size());
assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("NaN", ASSERT_EQUALS)).size());
assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getNaN", ASSERT_EQUALS)).size());
// assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("MAX_VALUE", ASSERT_EQUALS)).size());
// assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getMax_VALUE", ASSERT_EQUALS)).size());
assertEquals(1, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getMax_VALUE", ASSERT_EQUALS)).size());
}

@Test
public void testMultipleObservationsPoints() throws Exception {
public void testMultipleObservationsPoints() {
CtClass testClass = Utils.findClass("fr.inria.multipleobservations.TestClassToBeTest");
CtMethod test = Utils.findMethod("fr.inria.multipleobservations.TestClassToBeTest", "test");
List<CtMethod<?>> test_buildNewAssert = assertGenerator.assertionAmplification(testClass, Collections.singletonList(test));
Expand All @@ -136,9 +139,8 @@ public void testMultipleObservationsPoints() throws Exception {
}



@Test
public void testBuildAssertOnSpecificCases() throws Exception {
public void testBuildAssertOnSpecificCases() {
CtClass testClass = Utils.findClass("fr.inria.sample.TestClassWithSpecificCaseToBeAsserted");
CtMethod test1 = Utils.findMethod("fr.inria.sample.TestClassWithSpecificCaseToBeAsserted", "test1");
List<CtMethod<?>> test1_buildNewAssert = assertGenerator.assertionAmplification(testClass, Collections.singletonList(test1));
Expand All @@ -161,7 +163,7 @@ public void testBuildAssertOnSpecificCases() throws Exception {
private static final Logger LOGGER = LoggerFactory.getLogger(AssertGeneratorTest.class);

@Test
public void testBuildNewAssert() throws Exception {
public void testBuildNewAssert() {
LOGGER.info("Running testBuildNewAssert");

/*
Expand All @@ -170,6 +172,7 @@ public void testBuildNewAssert() throws Exception {
- primitive type and String (assertEquals)
- null value (assertNull)
- Collection: with elements (assertTrue(contains())) and empty (assertTrue(isEmpty()))
- Iterable
//TODO support generation of assertion on array
*/
CtClass<?> testClass = Utils.findClass("fr.inria.sample.TestClassWithoutAssert");
Expand All @@ -179,6 +182,7 @@ public void testBuildNewAssert() throws Exception {
fail("the assertion amplification should have result with at least one test.");
}
CtMethod<?> amplifiedTestMethod = ctMethods.get(0);
LOGGER.info("{}", amplifiedTestMethod.toString());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getBoolean", ASSERT_TRUE)).size());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getByte", ASSERT_EQUALS)).size());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getShort", ASSERT_EQUALS)).size());
Expand All @@ -191,8 +195,24 @@ public void testBuildNewAssert() throws Exception {
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getString", ASSERT_EQUALS)).size());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getChar", ASSERT_EQUALS)).size());
assertEquals(4, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("contains", ASSERT_TRUE)).size());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("isEmpty", ASSERT_TRUE)).size());
assertEquals(2, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getNull", ASSERT_NULL)).size());
assertEquals(4, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("isEmpty", ASSERT_TRUE)).size());
// must not match
assertEquals(0, amplifiedTestMethod.getElements(new AssertionFilterNameOnInvocation("getEmptyMyIterable", ASSERT_TRUE)).size());
// check that the amplified test method can be compiled
final CtClass<?> clone = testClass.clone();
clone.addMethod(amplifiedTestMethod);
DSpotUtils.printCtTypeToGivenDirectory(clone, Utils.getCompiler().getSourceOutputDirectory());
final List<CategorizedProblem> categorizedProblems = Utils.getCompiler().compileAndReturnProblems(InputConfiguration.get().getClasspathClassesProject());
assertTrue(
categorizedProblems.stream().filter(categorizedProblem ->
categorizedProblem.getMessage().startsWith(THE_METHOD_IS_EMPTY_IS_UNDEFINED_FOR_THE_TYPE))
.map(CategorizedProblem::toString).collect(Collectors.joining(AmplificationHelper.LINE_SEPARATOR)),
categorizedProblems.stream()
.noneMatch(categorizedProblem ->
categorizedProblem.getMessage().startsWith(THE_METHOD_IS_EMPTY_IS_UNDEFINED_FOR_THE_TYPE)
)
);
}

@Test
Expand Down
Loading

0 comments on commit 0a3c67b

Please sign in to comment.