Skip to content
Open
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,10 @@
package org.evomaster.client.java.controller.api.dto;

import org.evomaster.client.java.controller.api.dto.database.schema.ExtraConstraintsDto;
import org.evomaster.client.java.controller.api.dto.database.neo4j.Neo4jEntityDto;

import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -80,4 +82,9 @@ public class UnitsInfoDto {
* Extra information extracted for example from JPA entities
*/
public List<ExtraConstraintsDto> extraDatabaseConstraintsDtos;

/**
* Neo4j entities found in the SUT, one per class mapped to nodes. Empty if the SUT has none.
*/
public List<Neo4jEntityDto> neo4jEntities = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.evomaster.client.java.controller.api.dto.database.neo4j;

import java.util.ArrayList;
import java.util.List;

/**
* The shape of the nodes of one Neo4j entity of the SUT: its labels, properties and relationships.
* Neo4j has no schema, so this is read from the SUT's mapping classes.
*/
public class Neo4jEntityDto {

/**
* Fully qualified name of the entity class in the SUT.
*/
public String className;

/**
* Labels of the nodes of this entity, the primary one first.
*/
public List<String> labels = new ArrayList<>();

/**
* Properties of the nodes, in declaration order.
*/
public List<Neo4jEntityPropertyDto> properties = new ArrayList<>();

/**
* Relationships towards other entities, in declaration order.
*/
public List<Neo4jEntityRelationshipDto> relationships = new ArrayList<>();

/**
* Default constructor
*/
public Neo4jEntityDto() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.evomaster.client.java.controller.api.dto.database.neo4j;

import org.evomaster.client.java.controller.api.dto.database.operations.Neo4jPropertyTypeDto;

/**
* A property of the nodes of a Neo4j entity, as declared in the SUT's mapping of that entity.
*/
public class Neo4jEntityPropertyDto {

/**
* Name of the property in the graph.
*/
public String name;

/**
* Type the property is stored as.
*/
public Neo4jPropertyTypeDto type;

/**
* Whether the property identifies the node.
*/
public Boolean isId;

/**
* Whether the value is generated by the database or the mapping, so the SUT never assigns it.
*/
public Boolean isGenerated;

/**
* Smallest value the property can hold when the SUT reads it back, if its Java type is narrower
* than the graph's integer; null otherwise.
*/
public Long minValue;

/**
* Largest value the property can hold when the SUT reads it back, if its Java type is narrower
* than the graph's integer; null otherwise.
*/
public Long maxValue;

/**
* The only values the property accepts, when it is backed by an enum; empty otherwise.
*/
public java.util.List<String> enumValues = new java.util.ArrayList<>();

/**
* Default constructor
*/
public Neo4jEntityPropertyDto() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.evomaster.client.java.controller.api.dto.database.neo4j;

/**
* A relationship declared in a Neo4j entity, towards another entity.
*/
public class Neo4jEntityRelationshipDto {

/**
* Relationship type in the graph.
*/
public String type;

/**
* Labels of the entity at the other end, its primary label first.
*/
public java.util.List<String> targetLabels = new java.util.ArrayList<>();

/**
* Whether the relationship starts from this entity (outgoing) or arrives to it.
*/
public Boolean isOutgoing;

/**
* Default constructor
*/
public Neo4jEntityRelationshipDto() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@
import java.net.InetSocketAddress;
import java.sql.Connection;
import java.sql.SQLException;
import org.evomaster.client.java.controller.api.dto.database.neo4j.Neo4jEntityDto;
import org.evomaster.client.java.controller.api.dto.database.neo4j.Neo4jEntityPropertyDto;
import org.evomaster.client.java.controller.api.dto.database.neo4j.Neo4jEntityRelationshipDto;
import org.evomaster.client.java.controller.api.dto.database.operations.Neo4jPropertyTypeDto;
import org.evomaster.client.java.instrumentation.Neo4jEntity;
import org.evomaster.client.java.instrumentation.Neo4jEntityProperty;
import org.evomaster.client.java.instrumentation.Neo4jEntityRelationship;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -1975,10 +1982,52 @@ protected UnitsInfoDto getUnitsInfoDto(UnitsInfoRecorder recorder){
jpa.constraints = ec;
return jpa;
}).collect(Collectors.toList());
dto.neo4jEntities = toNeo4jEntityDtos(recorder.getNeo4jEntities());

return dto;
}

/**
* Relationships point to a class in the recorder, and to the labels of that class in the DTO, as the
* core only ever sees labels.
*/
private static List<Neo4jEntityDto> toNeo4jEntityDtos(List<Neo4jEntity> entities) {
Map<String, List<String>> labelsByClass = new HashMap<>();
for (Neo4jEntity e : entities) {
labelsByClass.put(e.getClassName(), e.getLabels());
}
List<Neo4jEntityDto> dtos = new ArrayList<>();
for (Neo4jEntity e : entities) {
Neo4jEntityDto dto = new Neo4jEntityDto();
dto.className = e.getClassName();
dto.labels = new ArrayList<>(e.getLabels());
for (Neo4jEntityProperty p : e.getProperties()) {
Neo4jEntityPropertyDto pd = new Neo4jEntityPropertyDto();
pd.name = p.getName();
pd.type = Neo4jPropertyTypeDto.valueOf(p.getType());
pd.isId = p.isId();
pd.isGenerated = p.isGenerated();
pd.minValue = p.getMinValue();
pd.maxValue = p.getMaxValue();
pd.enumValues = new ArrayList<>(p.getEnumValues());
dto.properties.add(pd);
}
for (Neo4jEntityRelationship r : e.getRelationships()) {
List<String> target = labelsByClass.get(r.getTargetClassName());
if (target == null) {
continue;
}
Neo4jEntityRelationshipDto rd = new Neo4jEntityRelationshipDto();
rd.type = r.getType();
rd.targetLabels = new ArrayList<>(target);
rd.isOutgoing = r.isOutgoing();
dto.relationships.add(rd);
}
dtos.add(dto);
}
return dtos;
}

@Override
public Object getRPCClient(String interfaceName) {
if (!(getProblemInfo() instanceof RPCProblem))
Expand Down
6 changes: 6 additions & 0 deletions client-java/instrumentation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>6.1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public static void doAnalyze(Collection<String> classNames) {
try {
ClassLoader loader = UnitsInfoRecorder.getInstance().getFirstClassLoader(name);
if (loader == null) {
//could happen in tests
loader = ClassAnalyzer.class.getClassLoader();
SimpleLogger.warn("No class loader registered for " + name);
}
Expand All @@ -77,6 +76,50 @@ public static void doAnalyze(Collection<String> classNames) {
}
}

/**
* Reads the Spring Data Neo4j mapping of the given classes, registering an entity for each class
* annotated with {@code @Node}. Does nothing if the SUT does not use Spring Data Neo4j.
*
* @param classNames the classes to look at
*/
public static void doAnalyzeNeo4jEntities(Collection<String> classNames) {

if (!classesCanBeLoaded(Neo4jEntityAnalyzer.SPRING_DATA_NEO4J_NAMES)) {
return;
}

for (String name : classNames) {
try {
Class<?> klass = loadClass(name);
if (klass == null) {
continue;
}
Neo4jEntity entity = Neo4jEntityAnalyzer.analyze(klass);
if (entity != null) {
UnitsInfoRecorder.registerNewNeo4jEntity(entity);
}
} catch (Exception e) {
SimpleLogger.error("Failed to analyze Neo4j mapping of " + name, e);
}
}
}

/**
* @return the class, loaded with the loader it was first seen with, or null if it cannot be loaded
*/
private static Class<?> loadClass(String name) {
try {
ClassLoader loader = UnitsInfoRecorder.getInstance().getFirstClassLoader(name);
if (loader == null) {
loader = ClassAnalyzer.class.getClassLoader();
}
return loader.loadClass(name);
} catch (ClassNotFoundException e) {
SimpleLogger.error("Failed to load class " + name, e);
return null;
}
}

private static boolean canUseJavaxJPA() {
boolean canUseJavaxJPA = classesCanBeLoaded(JAVAX_NAMES);
if (!canUseJavaxJPA) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.evomaster.client.java.instrumentation;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* The shape of the nodes the SUT maps to one of its entity classes: labels, properties and relationships.
* Neo4j has no schema of its own, so this is what stands in for one.
*/
public class Neo4jEntity implements Serializable {

/** Fully qualified name of the entity class. */
private final String className;

/** Labels of the nodes of this entity, the primary one first. */
private final List<String> labels;

/** Properties of the nodes, in declaration order. */
private final List<Neo4jEntityProperty> properties;

/** Relationships to other entities, in declaration order. */
private final List<Neo4jEntityRelationship> relationships;

/**
* @param className entity class
* @param labels labels of its nodes, at least one
* @param properties its properties
* @param relationships its relationships to other entities
*/
public Neo4jEntity(String className, List<String> labels, List<Neo4jEntityProperty> properties,
List<Neo4jEntityRelationship> relationships) {
this.className = Objects.requireNonNull(className);
if (labels == null || labels.isEmpty()) {
throw new IllegalArgumentException("An entity needs at least one label: " + className);
}
this.labels = Collections.unmodifiableList(labels);
this.properties = Collections.unmodifiableList(Objects.requireNonNull(properties));
this.relationships = Collections.unmodifiableList(Objects.requireNonNull(relationships));
}

public String getClassName() {
return className;
}

public List<String> getLabels() {
return labels;
}

public List<Neo4jEntityProperty> getProperties() {
return properties;
}

public List<Neo4jEntityRelationship> getRelationships() {
return relationships;
}

@Override
public String toString() {
return className + labels + " " + properties + " " + relationships;
}
}
Loading
Loading