diff --git a/dspot/src/main/java/eu/stamp_project/compare/MethodsHandler.java b/dspot/src/main/java/eu/stamp_project/compare/MethodsHandler.java index 1d8d0b97f..8f7b1088d 100644 --- a/dspot/src/main/java/eu/stamp_project/compare/MethodsHandler.java +++ b/dspot/src/main/java/eu/stamp_project/compare/MethodsHandler.java @@ -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"); diff --git a/dspot/src/main/java/eu/stamp_project/compare/ObjectLog.java b/dspot/src/main/java/eu/stamp_project/compare/ObjectLog.java index dd1201e7d..f4a586f8e 100644 --- a/dspot/src/main/java/eu/stamp_project/compare/ObjectLog.java +++ b/dspot/src/main/java/eu/stamp_project/compare/ObjectLog.java @@ -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, @@ -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()); diff --git a/dspot/src/main/java/eu/stamp_project/compare/Utils.java b/dspot/src/main/java/eu/stamp_project/compare/Utils.java index 9186dc794..ac87cd123 100644 --- a/dspot/src/main/java/eu/stamp_project/compare/Utils.java +++ b/dspot/src/main/java/eu/stamp_project/compare/Utils.java @@ -11,7 +11,7 @@ public class Utils { protected static Set> WRAPPER_TYPES = getWrapperTypes(); - public static boolean isPrimitiveCollectionOrMap(Object collectionOrMap) { + public static boolean isNonEmptyPrimitiveCollectionOrMap(Object collectionOrMap) { try { return collectionOrMap != null && (isNonEmptyPrimitiveCollection(collectionOrMap) || isNonEmptyPrimitiveMap(collectionOrMap)); diff --git a/dspot/src/main/java/eu/stamp_project/dspot/assertgenerator/Translator.java b/dspot/src/main/java/eu/stamp_project/dspot/assertgenerator/Translator.java index 2c98291b5..ee7e730ae 100644 --- a/dspot/src/main/java/eu/stamp_project/dspot/assertgenerator/Translator.java +++ b/dspot/src/main/java/eu/stamp_project/dspot/assertgenerator/Translator.java @@ -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 @@ -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); @@ -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("()"); @@ -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> getAllMethods(CtType type) { - try { - return type.getAllMethods(); - } catch (StackOverflowError e) { - LOGGER.error("Overflow!"); - final Set> 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]); diff --git a/dspot/src/main/resources/compare/MethodsHandler.class b/dspot/src/main/resources/compare/MethodsHandler.class index 01d1e5bdc..c48036e3a 100644 Binary files a/dspot/src/main/resources/compare/MethodsHandler.class and b/dspot/src/main/resources/compare/MethodsHandler.class differ diff --git a/dspot/src/main/resources/compare/ObjectLog.class b/dspot/src/main/resources/compare/ObjectLog.class index d47289ac4..0b66ce1a2 100644 Binary files a/dspot/src/main/resources/compare/ObjectLog.class and b/dspot/src/main/resources/compare/ObjectLog.class differ diff --git a/dspot/src/main/resources/compare/Utils.class b/dspot/src/main/resources/compare/Utils.class index ee921aec0..8e1745f39 100644 Binary files a/dspot/src/main/resources/compare/Utils.class and b/dspot/src/main/resources/compare/Utils.class differ diff --git a/dspot/src/test/java/eu/stamp_project/compare/ObjectLogTest.java b/dspot/src/test/java/eu/stamp_project/compare/ObjectLogTest.java index e553c355b..a314b1c6b 100644 --- a/dspot/src/test/java/eu/stamp_project/compare/ObjectLogTest.java +++ b/dspot/src/test/java/eu/stamp_project/compare/ObjectLogTest.java @@ -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; @@ -63,6 +60,12 @@ public int random() { public String getAbsolutePath() { return new File(".").getAbsolutePath(); } + public Iterable getIterable() { + return Collections.singleton(""); + } + public Collection getCollection() { + return Collections.singleton(""); + } } public static Integer add(Integer a, Integer b) { @@ -84,7 +87,7 @@ public void test() throws Exception { assertNotNull(add__0); // assertEquals(1, add__0.getNotDeterministValues().size()); final Map 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()")); diff --git a/dspot/src/test/java/eu/stamp_project/compare/UtilsTest.java b/dspot/src/test/java/eu/stamp_project/compare/UtilsTest.java index a5763617c..59611e1d6 100644 --- a/dspot/src/test/java/eu/stamp_project/compare/UtilsTest.java +++ b/dspot/src/test/java/eu/stamp_project/compare/UtilsTest.java @@ -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; @@ -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 list = new ArrayList<>(); - assertFalse(Utils.isPrimitiveCollectionOrMap(list)); + assertFalse(Utils.isNonEmptyPrimitiveCollectionOrMap(list)); list.add(1); - assertTrue(Utils.isPrimitiveCollectionOrMap(list)); + assertTrue(Utils.isNonEmptyPrimitiveCollectionOrMap(list)); final HashMap 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 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 diff --git a/dspot/src/test/java/eu/stamp_project/dspot/assertgenerator/AssertGeneratorTest.java b/dspot/src/test/java/eu/stamp_project/dspot/assertgenerator/AssertGeneratorTest.java index 9a64c0cea..a44657579 100644 --- a/dspot/src/test/java/eu/stamp_project/dspot/assertgenerator/AssertGeneratorTest.java +++ b/dspot/src/test/java/eu/stamp_project/dspot/assertgenerator/AssertGeneratorTest.java @@ -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; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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> test_buildNewAssert = assertGenerator.assertionAmplification(testClass, Collections.singletonList(test)); @@ -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> test1_buildNewAssert = assertGenerator.assertionAmplification(testClass, Collections.singletonList(test1)); @@ -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"); /* @@ -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"); @@ -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()); @@ -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 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 diff --git a/dspot/src/test/resources/sample/src/main/java/fr/inria/sample/ClassWithBoolean.java b/dspot/src/test/resources/sample/src/main/java/fr/inria/sample/ClassWithBoolean.java index 6512f6bb0..a646b5485 100644 --- a/dspot/src/test/resources/sample/src/main/java/fr/inria/sample/ClassWithBoolean.java +++ b/dspot/src/test/resources/sample/src/main/java/fr/inria/sample/ClassWithBoolean.java @@ -1,8 +1,6 @@ package fr.inria.sample; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Random; +import java.util.*; /** * User: Simon @@ -66,6 +64,92 @@ public Collection getListWithElements() { return strings; } + public Iterable getEmptyCollectionAsIterable() { + return new ArrayList<>(); + } + + public class MyIterable implements Iterable { + @Override + public Iterator iterator() { + return null; + } + } + + public Iterable getEmptyMyIterable() { + return new MyIterable(); + } + + public class MyList implements Collection { + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Object[] toArray() { + return new Object[0]; + } + + @Override + public T[] toArray(T[] a) { + return null; + } + + @Override + public boolean add(String s) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public boolean containsAll(Collection c) { + return false; + } + + @Override + public boolean addAll(Collection c) { + return false; + } + + @Override + public boolean removeAll(Collection c) { + return false; + } + + @Override + public boolean retainAll(Collection c) { + return false; + } + + @Override + public void clear() { + + } + } + + public Iterable getEmptyMyListAsIterable() { + return new MyList(); + } + public boolean getBoolean() { Random r = new Random(23L); return r.nextBoolean();