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
29 changes: 29 additions & 0 deletions core/src/main/kotlin/org/evomaster/core/EMConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,16 @@ class EMConfig {
"'extractDynamoDbExecutionInfo'")
}

if (shouldGenerateCassandraData() && !heuristicsForCassandra) {
throw ConfigProblemException("Cannot generate Cassandra data if you did not enable " +
"collecting heuristics with 'heuristicsForCassandra'")
}

if (shouldGenerateCassandraData() && !extractCassandraExecutionInfo) {
throw ConfigProblemException("Cannot generate Cassandra data if you did not enable " +
"extracting Cassandra execution info with 'extractCassandraExecutionInfo'")
}

if (enableTrackEvaluatedIndividual && enableTrackIndividual) {
throw ConfigProblemException("When tracking EvaluatedIndividual, it is not necessary to track individual")
}
Expand Down Expand Up @@ -1088,6 +1098,8 @@ class EMConfig {

fun shouldGenerateNeo4jData() = isUsingAdvancedTechniques() && generateNeo4jData

fun shouldGenerateCassandraData() = isUsingAdvancedTechniques() && generateCassandraData

fun dtoSupportedForPayload() = dtoForRequestPayload && couldSupportDtoForPayload()

fun couldSupportDtoForPayload() = problemType == ProblemType.REST && outputFormat.isJavaOrKotlin()
Expand Down Expand Up @@ -2096,6 +2108,12 @@ class EMConfig {
@DependsOnFalseFor("blackBox")
var heuristicsForNeo4j = false

@Experimental
@Cfg("Tracking of Cassandra commands to improve test generation")
@DependsOnFalseFor("blackBox")
@DependsOnTrueFor("extractCassandraExecutionInfo")
var heuristicsForCassandra = false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't have a @DependsOnTrueFor for extractSqlExecutionInfo ?


@Cfg("Enable extracting SQL execution info")
@DependsOnFalseFor("blackBox")
var extractSqlExecutionInfo = true
Expand All @@ -2119,6 +2137,11 @@ class EMConfig {
@DependsOnFalseFor("blackBox")
var extractNeo4jExecutionInfo = false

@Experimental
@Cfg("Enable extracting Cassandra execution info")
@DependsOnFalseFor("blackBox")
var extractCassandraExecutionInfo = false

@Experimental
@Cfg("Enable EvoMaster to generate SQL data with direct accesses to the database. Use the Z3 SMT solver")
@DependsOnFalseFor("blackBox")
Expand Down Expand Up @@ -2190,6 +2213,12 @@ class EMConfig {
@DependsOnTrueFor("extractNeo4jExecutionInfo")
var generateNeo4jData = false

@Experimental
@Cfg("Enable EvoMaster to generate Cassandra data with direct accesses to the database")
@DependsOnFalseFor("blackBox")
@DependsOnTrueFor("extractCassandraExecutionInfo")
var generateCassandraData = false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't have a @DependsOnTrueFor for extractSqlExecutionInfo ?


@Cfg("When generating SQL data, how many new rows (max) to generate for each specific SQL Select")
@Min(1.0)
@DependsOnFalseFor("blackBox")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.evomaster.core.database.cassandra

import org.evomaster.client.java.controller.api.dto.database.execution.CassandraExecutionsDto
import org.evomaster.client.java.controller.api.dto.database.execution.CassandraFailedQuery

/**
* Encapsulates every CQL query that matched no row during an execution, as a data-generation hint.
*/
class CassandraExecution(
/**
* One entry per CQL query that matched no row,
* each with the table it targeted and the shape of its rows.
*/
val failedQueries: MutableList<CassandraFailedQuery>?
) {

companion object {

fun fromDto(dto: CassandraExecutionsDto?): CassandraExecution {
return CassandraExecution(dto?.failedQueries?.toMutableList())
}
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
package org.evomaster.core.problem.api.service

import com.google.inject.Inject
import org.evomaster.client.java.controller.api.dto.database.execution.DynamoDbFailedQuery
import org.evomaster.client.java.controller.api.dto.database.execution.MongoFailedQuery
import org.evomaster.client.java.controller.api.dto.database.execution.RedisFailedCommand
import org.evomaster.client.java.controller.api.dto.database.execution.*
import org.evomaster.client.java.instrumentation.shared.ExternalServiceSharedUtils
import org.evomaster.core.EMConfig
import org.evomaster.core.Lazy
import org.evomaster.core.database.cassandra.CassandraDbAction
import org.evomaster.core.database.cassandra.CassandraInsertBuilder
import org.evomaster.core.database.dynamodb.DynamoDbAction
import org.evomaster.core.database.dynamodb.DynamoDbInsertBuilder
import org.evomaster.core.database.mongo.MongoDbAction
import org.evomaster.core.database.neo4j.Neo4jDbAction
import org.evomaster.core.database.neo4j.Neo4jInsertBuilder
import org.evomaster.core.database.redis.RedisDbAction
import org.evomaster.core.database.redis.RedisInsertBuilder
import org.evomaster.core.database.sql.SqlAction
import org.evomaster.core.database.sql.SqlActionUtils
import org.evomaster.core.database.sql.SqlInsertBuilder
import org.evomaster.core.database.sql.schema.TableId
import org.evomaster.core.database.sql.solver.service.SMTLibZ3DbConstraintSolver
import org.evomaster.core.problem.api.ApiWsIndividual
import org.evomaster.core.problem.enterprise.EnterpriseActionGroup
import org.evomaster.core.problem.externalservice.HostnameResolutionAction
import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction
import org.evomaster.core.problem.externalservice.httpws.param.HttpWsResponseParam
import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHttpWsResponseHandler
import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalServiceHandler
import org.evomaster.core.database.redis.RedisDbAction
import org.evomaster.core.database.redis.RedisInsertBuilder
import org.evomaster.core.database.dynamodb.DynamoDbAction
import org.evomaster.core.database.dynamodb.DynamoDbInsertBuilder
import org.evomaster.core.database.neo4j.Neo4jDbAction
import org.evomaster.core.database.neo4j.Neo4jInsertBuilder
import org.evomaster.client.java.controller.api.dto.database.execution.Neo4jFailedQueryDto
import org.evomaster.core.search.EvaluatedIndividual
import org.evomaster.core.search.GroupsOfChildren
import org.evomaster.core.search.Individual
Expand All @@ -31,11 +35,6 @@ import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene
import org.evomaster.core.search.impact.impactinfocollection.ImpactsOfIndividual
import org.evomaster.core.search.service.mutator.MutatedGeneSpecification
import org.evomaster.core.search.service.mutator.StructureMutator
import org.evomaster.core.database.sql.solver.service.SMTLibZ3DbConstraintSolver
import org.evomaster.core.database.sql.SqlAction
import org.evomaster.core.database.sql.SqlActionUtils
import org.evomaster.core.database.sql.SqlInsertBuilder
import org.evomaster.core.database.sql.schema.TableId
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import kotlin.math.max
Expand Down Expand Up @@ -203,6 +202,7 @@ abstract class ApiWsStructureMutator : StructureMutator() {
addInitializingRedisDbActions(individual, mutatedGenes, sampler)
addInitializingDynamoDbActions(individual, mutatedGenes, sampler)
addInitializingNeo4jDbActions(individual, mutatedGenes, sampler)
addInitializingCassandraDbActions(individual, mutatedGenes, sampler)
addInitializingHostnameResolutionActions(individual, mutatedGenes, sampler)
// TODO if we handle schedule actions with structure mutator
}
Expand Down Expand Up @@ -345,6 +345,41 @@ abstract class ApiWsStructureMutator : StructureMutator() {
}
}

private fun <T: ApiWsIndividual> addInitializingCassandraDbActions(
individual: EvaluatedIndividual<*>,
mutatedGenes: MutatedGeneSpecification?,
sampler: ApiWsSampler<T>
) {
if (!config.shouldGenerateCassandraData()) {
return
}

val ind = individual.individual as? T
?: throw IllegalArgumentException("Invalid individual type")

val failedQueries = individual.fitness.getViewOfAggregatedFailedCassandraQueries()

if (failedQueries.isEmpty()) {
return
}

val oldCassandraDbActions = mutableListOf<EnvironmentAction>().plus(ind.seeInitializingActions())

val addedCassandraDbInsertions = handleFailedCql(ind, failedQueries, mutatedGenes, sampler)

ind.repairInitializationActions(randomness)
// update impact based on added genes
if (mutatedGenes != null && config.isEnabledArchiveGeneSelection()) {
individual.updateImpactGeneDueToAddedInitializationGenes(
mutatedGenes,
oldCassandraDbActions,
addedCassandraDbInsertions,
ImpactsOfIndividual.CASSANDRADB_ACTION_KEY,
config
)
}
}

private fun <T : ApiWsIndividual> addInitializingHostnameResolutionActions(
individual: EvaluatedIndividual<*>,
mutatedGenes: MutatedGeneSpecification?,
Expand Down Expand Up @@ -660,6 +695,28 @@ abstract class ApiWsStructureMutator : StructureMutator() {
return addedActions
}

private fun <T : ApiWsIndividual> handleFailedCql(
ind: T,
failedQueries: List<CassandraFailedQuery>,
mutatedGenes: MutatedGeneSpecification?,
sampler: ApiWsSampler<T>
): MutableList<List<CassandraDbAction>>? {

val builder = CassandraInsertBuilder()
val addedCassandraDbInsertions = if (mutatedGenes != null) mutableListOf<List<CassandraDbAction>>() else null

failedQueries
.mapNotNull { it.tableSchema }
.filter { builder.canBuildInsertionFor(it) }
.forEach {
val insertion = listOf(sampler.sampleCassandraInsertion(it))
ind.addInitializingCassandraDbActions(actions = insertion)
addedCassandraDbInsertions?.add(insertion)
}

return addedCassandraDbInsertions
}

private fun findMissing(
fw: Map<TableId, Set<String>>,
dbactions: List<SqlAction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.evomaster.core.StaticCounter
import org.evomaster.core.database.cassandra.CassandraDbAction
import org.evomaster.core.database.cassandra.CassandraDbActionResult
import org.evomaster.core.database.cassandra.CassandraDbActionTransformer
import org.evomaster.core.database.cassandra.CassandraExecution
import org.evomaster.core.database.mongo.MongoDbAction
import org.evomaster.core.database.mongo.MongoDbActionResult
import org.evomaster.core.database.mongo.MongoDbActionTransformer
Expand Down Expand Up @@ -537,6 +538,18 @@ abstract class EnterpriseFitness<T> : FitnessFunction<T>() where T : Individual
}
fv.aggregateNeo4jDatabaseData()
}

if (configuration.heuristicsForCassandra) {
handleCassandraHeuristics(dto, fv)
}

if (configuration.extractCassandraExecutionInfo) {
for (i in 0 until dto.extraHeuristics.size) {
val extra = dto.extraHeuristics[i]
fv.setCassandraExecution(i, CassandraExecution.fromDto(extra.cassandraExecutionsDto))
}
fv.aggregateCassandraDatabaseData()
}
}

private fun handleSqlHeuristics(
Expand Down Expand Up @@ -646,6 +659,40 @@ abstract class EnterpriseFitness<T> : FitnessFunction<T>() where T : Individual
}
}

/** Applies CQL WHERE distances and records their evaluation metrics. */
private fun handleCassandraHeuristics(dto: TestResultsDto, fv: FitnessValue) {
for (i in 0 until dto.extraHeuristics.size) {

val extra = dto.extraHeuristics[i]

extraHeuristicsLogger.writeHeuristics(extra.heuristics, i)

val toMinimize = extra.heuristics
.filter {
it != null
&& it.objective == ExtraHeuristicEntryDto.Objective.MINIMIZE_TO_ZERO
&& it.type == ExtraHeuristicEntryDto.Type.CASSANDRA
}.map { it.value }
.toList()

if (toMinimize.isNotEmpty()) {
fv.addExtraObjectivesToMinimize(i, toMinimize)
}

extra.heuristics
.filterNotNull().forEach {
if (it.type == ExtraHeuristicEntryDto.Type.CASSANDRA) {
statistics.reportNumberOfEvaluatedRowsForCassandraHeuristic(it.numberOfEvaluatedRecords)
if (it.extraHeuristicEvaluationFailure) {
statistics.reportCassandraHeuristicEvaluationFailure()
} else {
statistics.reportCassandraHeuristicEvaluationSuccess()
}
}
}
}
}

/** Applies DynamoDB predicate distances and records their evaluation metrics. */
private fun handleDynamoDbHeuristics(dto: TestResultsDto, fv: FitnessValue) {
for (i in 0 until dto.extraHeuristics.size) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import com.google.inject.Inject
import org.evomaster.client.java.controller.api.dto.SutInfoDto
import org.evomaster.client.java.controller.api.dto.problem.param.DerivedParamChangeReqDto
import org.evomaster.client.java.controller.api.dto.problem.param.RestDerivedParamDto
import org.evomaster.client.java.controller.api.dto.database.cassandra.CassandraTableSchemaDto
import org.evomaster.core.database.cassandra.CassandraDbAction
import org.evomaster.core.database.cassandra.CassandraInsertBuilder
import org.evomaster.core.database.mongo.MongoDbAction
import org.evomaster.core.database.mongo.MongoInsertBuilder
import org.evomaster.core.output.OutputFormat
Expand Down Expand Up @@ -133,6 +136,12 @@ abstract class EnterpriseSampler<T> : Sampler<T>() where T : Individual {
return action
}

fun sampleCassandraInsertion(tableSchema: CassandraTableSchemaDto): CassandraDbAction {
val action = CassandraInsertBuilder().createCassandraInsertionAction(tableSchema)
action.seeTopGenes().forEach{it.doInitialize(randomness)}
return action
}

fun canInsertInto(tableName: TableId) : Boolean {
//TODO might need to refactor/remove once we deal with VIEWs
return sqlInsertBuilder?.isTable(tableName) ?: false
Expand Down
Loading
Loading