From 2dda8cb7acb1bb358b518df803e97883b57d8438 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 10 Apr 2026 21:24:15 +0200 Subject: [PATCH 01/12] refactoring --- .../core/output/service/RestTestCaseWriter.kt | 11 ++-- .../rest/builder/RestActionBuilderV3.kt | 12 ++-- .../core/problem/rest/data/RestCallAction.kt | 9 +++ .../service/fitness/AbstractRestFitness.kt | 11 +--- .../core/search/gene/collection/EnumGene.kt | 5 +- .../gene/interfaces/NamedExamplesGene.kt | 10 ---- .../gene/interfaces/UserExamplesGene.kt | 36 +++++++++++ .../core/search/gene/wrapper/ChoiceGene.kt | 5 +- .../problem/rest/RestActionBuilderV3Test.kt | 19 ++++++ .../examples_multi_params.yaml | 60 +++++++++++++++++++ 10 files changed, 139 insertions(+), 39 deletions(-) delete mode 100644 core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/NamedExamplesGene.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt create mode 100644 core/src/test/resources/swagger/artificial/defaultandexamples/examples_multi_params.yaml diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt index 0f5b4a5869..0a592c26f0 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt @@ -10,7 +10,6 @@ import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.enterprise.EnterpriseActionResult import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.HttpWsCallResult -import org.evomaster.core.problem.rest.builder.RestActionBuilderV3 import org.evomaster.core.problem.rest.data.RestCallAction import org.evomaster.core.problem.rest.data.RestCallResult import org.evomaster.core.problem.rest.data.RestIndividual @@ -24,14 +23,11 @@ import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual -import org.evomaster.core.search.gene.collection.EnumGene -import org.evomaster.core.search.gene.interfaces.NamedExamplesGene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.utils.GeneUtils -import org.evomaster.core.search.gene.wrapper.ChoiceGene import org.evomaster.core.utils.StringUtils import org.slf4j.LoggerFactory import java.nio.file.Path -import java.util.* class RestTestCaseWriter : HttpWsTestCaseWriter { @@ -555,10 +551,11 @@ class RestTestCaseWriter : HttpWsTestCaseWriter { private fun getAllUsedExamples(ind: RestIndividual) : List{ return ind.seeFullTreeGenes() - .filter { it.name == RestActionBuilderV3.EXAMPLES_NAME } + .filter { it is UserExamplesGene && it.isUsedForExamples() } .filter { it.staticCheckIfImpactPhenotype() } .map { - val name = if(it is NamedExamplesGene){ + val name = if(it is UserExamplesGene){ + //always true "(${it.getValueName()?: "-"}) " } else { "" diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/builder/RestActionBuilderV3.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/builder/RestActionBuilderV3.kt index 3cc76ead92..681684915c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/builder/RestActionBuilderV3.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/builder/RestActionBuilderV3.kt @@ -41,6 +41,7 @@ import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene import org.evomaster.core.search.gene.datetime.FormatForDatesAndTimes import org.evomaster.core.search.gene.datetime.TimeGene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.numeric.* import org.evomaster.core.search.gene.wrapper.ChoiceGene import org.evomaster.core.search.gene.wrapper.CustomMutationRateGene @@ -70,11 +71,6 @@ object RestActionBuilderV3 { private val log: Logger = LoggerFactory.getLogger(RestActionBuilderV3::class.java) - /** - * Name given to enum genes representing data examples coming from OpenAPI schema - */ - const val EXAMPLES_NAME = "SCHEMA_EXAMPLES" - private val refCache = mutableMapOf() /** @@ -1563,7 +1559,7 @@ object RestActionBuilderV3 { val n = examples.map{it.second} // names val exampleGene = if(examples.isNotEmpty()){ - ChoiceGene(EXAMPLES_NAME, v, valueNames = n) + ChoiceGene(UserExamplesGene.EXAMPLES_NAME, v, valueNames = n) } else null val defaultGene = if(defaultValue != null){ duplicateObjectWithExampleFields("default", mainGene, defaultValue) @@ -1769,12 +1765,12 @@ object RestActionBuilderV3 { val exampleGene = if(examples.isNotEmpty()){ when{ NumberGene::class.java.isAssignableFrom(geneClass) - -> EnumGene(EXAMPLES_NAME, v,0,true, n) + -> EnumGene(UserExamplesGene.EXAMPLES_NAME, v,0,true, n) geneClass == StringGene::class.java || geneClass == Base64StringGene::class.java || geneClass == RegexGene::class.java - -> EnumGene(EXAMPLES_NAME, v,0,false, n) + -> EnumGene(UserExamplesGene.EXAMPLES_NAME, v,0,false, n) //TODO Arrays else -> { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt index 100aecf54e..1d0ce0cf87 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt @@ -17,6 +17,7 @@ import org.evomaster.core.problem.rest.util.ParserUtil import org.evomaster.core.problem.util.BindingBuilder import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.wrapper.OptionalGene import org.evomaster.core.search.service.Randomness import java.net.URLEncoder @@ -452,4 +453,12 @@ class RestCallAction( this.weakReference = wr return copy } + + fun getNamedExamples() : Map { + return seeAllGenes() + .filterIsInstance() + .mapNotNull { it.getValueName() } + .groupingBy { it } + .eachCount() + } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt index cc5ffdb03f..1e0b9621ad 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt @@ -1,7 +1,6 @@ package org.evomaster.core.problem.rest.service.fitness import com.webfuzzing.commons.faults.DefinedFaultCategory -import com.webfuzzing.commons.faults.FaultCategory import org.evomaster.test.utils.EMTestUtils import org.evomaster.client.java.controller.api.dto.ActionDto import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto @@ -12,17 +11,14 @@ import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.enterprise.DetectedFault import org.evomaster.core.problem.enterprise.ExperimentalFaultCategory import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.problem.enterprise.auth.NoAuth import org.evomaster.core.problem.externalservice.HostnameResolutionAction import org.evomaster.core.problem.externalservice.HostnameResolutionInfo import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHttpWsResponseHandler import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalServiceHandler import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceInfo -import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.httpws.auth.AuthUtils import org.evomaster.core.problem.httpws.service.HttpWsFitness import org.evomaster.core.problem.rest.* -import org.evomaster.core.problem.rest.builder.RestActionBuilderV3 import org.evomaster.core.problem.rest.data.HttpVerb import org.evomaster.core.problem.rest.data.RestCallAction import org.evomaster.core.problem.rest.data.RestCallResult @@ -52,6 +48,7 @@ import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.EnumGene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.numeric.NumberGene import org.evomaster.core.search.gene.wrapper.ChoiceGene import org.evomaster.core.search.gene.wrapper.OptionalGene @@ -61,7 +58,6 @@ import org.evomaster.core.search.service.DataPool import org.evomaster.core.search.service.ExecutionStats import org.evomaster.core.search.service.SearchTimeController import org.evomaster.core.taint.TaintAnalysis -import org.evomaster.core.utils.StackTraceUtils import org.slf4j.Logger import org.slf4j.LoggerFactory import java.net.URI @@ -509,10 +505,9 @@ abstract class AbstractRestFitness : HttpWsFitness() { /* explicit targets for examples */ - val examples = call.seeTopGenes() - .flatMap { it.flatView() } + val examples = call.seeAllGenes() + .filter { it is UserExamplesGene && it.isUsedForExamples() } .filter { it.staticCheckIfImpactPhenotype() } - .filter { it.name == RestActionBuilderV3.EXAMPLES_NAME } examples.forEach { val name = (it.parent as Gene).name diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt index 30fb74912c..dd6c9cbbd9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt @@ -3,12 +3,11 @@ package org.evomaster.core.search.gene.collection import org.evomaster.core.output.OutputFormat import org.evomaster.core.search.gene.BooleanGene import org.evomaster.core.search.gene.Gene -import org.evomaster.core.search.gene.interfaces.NamedExamplesGene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.numeric.DoubleGene import org.evomaster.core.search.gene.numeric.FloatGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.numeric.LongGene -import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.root.SimpleGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.gene.wrapper.ChoiceGene @@ -43,7 +42,7 @@ class EnumGene>( * This is usually just extra information, eg, to recognize named "examples" in OpenAPI schemas */ private val valueNames: List? = null -) : SimpleGene(name), NamedExamplesGene { +) : SimpleGene(name), UserExamplesGene { companion object { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/NamedExamplesGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/NamedExamplesGene.kt deleted file mode 100644 index 272c22949e..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/NamedExamplesGene.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.evomaster.core.search.gene.interfaces - -/** - * A gene representing possible different examples provided by the user. - * Such examples might have unique names/ids used to easily identify them - */ -interface NamedExamplesGene { - - fun getValueName(): String? -} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt new file mode 100644 index 0000000000..611173de7c --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt @@ -0,0 +1,36 @@ +package org.evomaster.core.search.gene.interfaces + +import org.evomaster.core.search.gene.Gene + +/** + * A gene representing possible different examples provided by the user. + * Such examples might have unique names/ids used to easily identify them. + * + * Note: a gene that might be used for User-Examples does not mean that necessarily has user examples. + * + * This interface must be used only on Gene classes + */ +interface UserExamplesGene { + + companion object { + /** + * Name given to enum genes representing data examples coming from OpenAPI schema + */ + const val EXAMPLES_NAME = "SCHEMA_EXAMPLES" + } + + /** + * Check if this gene is used to store examples provided by the user + */ + fun isUsedForExamples() : Boolean{ + return (this as Gene).name == EXAMPLES_NAME + } + + /** + * The name of the chosen example, which is selected for the value of this gene. + * If there are no examples defined for this gene, or the chosen example is unnamed, then this function returns null. + */ + fun getValueName(): String? + +} + diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt index 71da8981c7..9e62fa0d5c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt @@ -1,9 +1,8 @@ package org.evomaster.core.search.gene.wrapper -import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.OutputFormat import org.evomaster.core.search.gene.Gene -import org.evomaster.core.search.gene.interfaces.NamedExamplesGene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.interfaces.WrapperGene import org.evomaster.core.search.gene.root.CompositeFixedGene import org.evomaster.core.search.gene.utils.GeneUtils @@ -35,7 +34,7 @@ class ChoiceGene( */ valueNames: List? = null, -) : CompositeFixedGene(name, geneChoices), NamedExamplesGene, WrapperGene where T : Gene { +) : CompositeFixedGene(name, geneChoices), UserExamplesGene, WrapperGene where T : Gene { companion object { private val log: Logger = LoggerFactory.getLogger(ChoiceGene::class.java) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt index e87a305434..ed0bf131a3 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt @@ -2236,4 +2236,23 @@ class RestActionBuilderV3Test{ } assertNotNull(x.getWrappedGene(NullableGene::class.java)) } + + + @Disabled + @Test + fun testMultiParamExamples(){ + + val map = loadAndAssertActions("/swagger/artificial/defaultandexamples/examples_multi_params.yaml", 1, true) + + val post = map["POST:/v2/api"] as RestCallAction + + val examples = post.getNamedExamples() + assertEquals(3, examples.size) + assertEquals(3, examples["Foo"]) + assertEquals(2, examples["Bar"]) + assertEquals(1, examples["Hello"]) + + + //TODO apply and check examples + } } diff --git a/core/src/test/resources/swagger/artificial/defaultandexamples/examples_multi_params.yaml b/core/src/test/resources/swagger/artificial/defaultandexamples/examples_multi_params.yaml new file mode 100644 index 0000000000..fbebb1d570 --- /dev/null +++ b/core/src/test/resources/swagger/artificial/defaultandexamples/examples_multi_params.yaml @@ -0,0 +1,60 @@ +--- +openapi: 3.1.0 +info: + title: examples_multi_params + description: examples_multi_params + version: 1.0.0 +servers: + - url: "/v2" +paths: + "/api": + post: + parameters: + - in: query + name: x + required: true + schema: + type: string + examples: + Bar: + value: xbar + Foo: + value: xfoo + - in: query + name: y + required: true + schema: + type: string + examples: + Bar: + value: ybar + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + id: + type: integer + name: + type: string + extra: + type: integer + examples: + Bar: + value: + id: 42 + name: nameB + Foo: + value: + id: 123 + name: nameF + extra: 77 + Hello: + value: + id: 667 + responses: + '200': + description: OK \ No newline at end of file From d2754edc0f7f7637938cc2d28432782f55d3abfa Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 10 Apr 2026 21:43:41 +0200 Subject: [PATCH 02/12] fixing test --- .../core/problem/rest/data/RestCallAction.kt | 3 ++- .../evomaster/core/search/gene/collection/EnumGene.kt | 4 ++++ .../core/search/gene/interfaces/UserExamplesGene.kt | 1 + .../evomaster/core/search/gene/wrapper/ChoiceGene.kt | 4 ++++ .../core/problem/rest/RestActionBuilderV3Test.kt | 11 +++++++---- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt index 1d0ce0cf87..3b0803e9e7 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt @@ -457,7 +457,8 @@ class RestCallAction( fun getNamedExamples() : Map { return seeAllGenes() .filterIsInstance() - .mapNotNull { it.getValueName() } + .filter { it.isUsedForExamples() } + .flatMap { it.getAvailableExampleNames() } .groupingBy { it } .eachCount() } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt index dd6c9cbbd9..217e559ca1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt @@ -202,6 +202,10 @@ class EnumGene>( return valueNames?.get(index) } + override fun getAvailableExampleNames() : Set { + return valueNames?.mapNotNull { it }?.toSet() ?: setOf() + } + /** * [EnumGene] can be used in DTOs when the API spec contains either example values or an enum. * Since the [EnumGene] uses generics to hold values, this function returns the type to be used diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt index 611173de7c..d2ce3d8f98 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt @@ -32,5 +32,6 @@ interface UserExamplesGene { */ fun getValueName(): String? + fun getAvailableExampleNames() : Set } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt index 9e62fa0d5c..e8d8a5683b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt @@ -182,6 +182,10 @@ class ChoiceGene( return valueNames?.get(activeGeneIndex) } + override fun getAvailableExampleNames() : Set { + return valueNames?.mapNotNull { it }?.toSet() ?: setOf() + } + /** * Copies the value of the other gene. The other gene * does not have to be [ChoiceGene]. diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt index ed0bf131a3..fe8a9f66e8 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt @@ -2238,18 +2238,21 @@ class RestActionBuilderV3Test{ } - @Disabled @Test fun testMultiParamExamples(){ - val map = loadAndAssertActions("/swagger/artificial/defaultandexamples/examples_multi_params.yaml", 1, true) + val map = loadAndAssertActions( + "/swagger/artificial/defaultandexamples/examples_multi_params.yaml", + 1, + RestActionBuilderV3.Options(enableConstraintHandling = true, probUseExamples = 0.5) + ) val post = map["POST:/v2/api"] as RestCallAction val examples = post.getNamedExamples() assertEquals(3, examples.size) - assertEquals(3, examples["Foo"]) - assertEquals(2, examples["Bar"]) + assertEquals(2, examples["Foo"]) + assertEquals(3, examples["Bar"]) assertEquals(1, examples["Hello"]) From fd73a7b6a42b9c062aaeb4b0046aa4ea8561edb9 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 10:06:13 +0200 Subject: [PATCH 03/12] refactoring --- .../core/search/service/mutator/Mutator.kt | 7 +-- .../search/service/mutator/StandardMutator.kt | 52 +++++++++++-------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt index 4576146e8b..66d926f9e2 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt @@ -92,7 +92,7 @@ abstract class Mutator : TrackOperator where T : Individual { /** * a set of actions/handling which are required to be executed before mutations */ - fun preActionBeforeMutatoin(evaluatedIndividual: EvaluatedIndividual){ + fun preHandlingBeforeMutation(evaluatedIndividual: EvaluatedIndividual){ /* tracking might be null if the current is never mutated then need to handle it before mutation @@ -100,7 +100,7 @@ abstract class Mutator : TrackOperator where T : Individual { preHandlingTrackedIndividual(evaluatedIndividual) } - open fun postActionAfterMutation(individual: T, mutated: MutatedGeneSpecification?){} + open fun postHandlingAfterMutation(individual: T, mutated: MutatedGeneSpecification?){} open fun update(previous: EvaluatedIndividual, mutated: EvaluatedIndividual, mutatedGenes: MutatedGeneSpecification?, mutationEvaluated: EvaluatedMutation){} @@ -127,7 +127,8 @@ abstract class Mutator : TrackOperator where T : Individual { var current = individual // we run pre-actions before upToNTimes mutations - preActionBeforeMutatoin(current) + // TODO is this still needed? done inside mutation anyway + preHandlingBeforeMutation(current) val targets = archive.notCoveredTargets().toMutableSet() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt index d9f910879b..2c2cf7ef83 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt @@ -179,10 +179,6 @@ open class StandardMutator : Mutator() where T : Individual { return toMutate } - private fun mutationPostProcessing(individual: T) { - sampler - } - private fun mutationPreProcessing(individual: T) { @@ -190,7 +186,7 @@ open class StandardMutator : Mutator() where T : Individual { TaintAnalysis.evolveIndividual(individual,applyEvolve,applyEvolve) for(a in individual.seeAllActions()){ - val update =if(a is ApiWsAction) { + val update = if(a is ApiWsAction) { a.parameters.find { it is UpdateForBodyParam } as? UpdateForBodyParam }else if (a is RPCExternalServiceAction){ a.responses.find { it is UpdateForParam } as? UpdateForParam @@ -304,28 +300,38 @@ open class StandardMutator : Mutator() where T : Individual { targets: Set, mutatedGenes: MutatedGeneSpecification? ): T { - preActionBeforeMutatoin(individual) + + preHandlingBeforeMutation(individual) // mutate the individual val mutatedIndividual = innerMutate(individual, targets, mutatedGenes) - postActionAfterMutation(mutatedIndividual, mutatedGenes) - -// if (config.trackingEnabled()) tag(mutatedIndividual, time.evaluatedIndividuals) + postHandlingAfterMutation(mutatedIndividual, mutatedGenes) return mutatedIndividual } - override fun postActionAfterMutation(mutatedIndividual: T, mutated: MutatedGeneSpecification?) { + /** + * Here we do actual mutations done on purpose... + * this is different from [postHandlingAfterMutation], where modifications + * are done to maintain and satisfy validity + */ + private fun mutationPostProcessing(individual: T) { + sampler + } + + + + override fun postHandlingAfterMutation(individual: T, mutated: MutatedGeneSpecification?) { Lazy.assert { SqlActionUtils.isValidForeignKeys( - mutatedIndividual.seeInitializingActions().filterIsInstance() + individual.seeInitializingActions().filterIsInstance() ) } Lazy.assert { - mutatedIndividual.seeAllActions() + individual.seeAllActions() .flatMap { it.seeTopGenes() } .all { GeneUtils.verifyRootInvariant(it) && @@ -334,30 +340,30 @@ open class StandardMutator : Mutator() where T : Individual { } // repair the initialization actions (if needed) - mutatedIndividual.repairInitializationActions(randomness) + individual.repairInitializationActions(randomness) //Check that the repair was successful - Lazy.assert { mutatedIndividual.isValidInitializationActions() } + Lazy.assert { individual.isValidInitializationActions() } /* In GraphQL, each boolean selection in Objects MUST have at least one filed selected */ - if (mutatedIndividual is GraphQLIndividual) { - GraphQLUtils.repairIndividual(mutatedIndividual) + if (individual is GraphQLIndividual) { + GraphQLUtils.repairIndividual(individual) } - if (!mutatedIndividual.verifyBindingGenes()) { - mutatedIndividual.cleanBrokenBindingReference() - Lazy.assert { mutatedIndividual.verifyBindingGenes() } + if (!individual.verifyBindingGenes()) { + individual.cleanBrokenBindingReference() + Lazy.assert { individual.verifyBindingGenes() } } - if (mutatedIndividual is RestIndividual) { - mutatedIndividual.repairDbActionsInCalls() - mutatedIndividual.fixResourceForwardLinks() + if (individual is RestIndividual) { + individual.repairDbActionsInCalls() + individual.fixResourceForwardLinks() } // update MutatedGeneSpecification after the post-handling - if(mutated?.repairInitAndDbSpecification(mutatedIndividual) == true){ + if(mutated?.repairInitAndDbSpecification(individual) == true){ LoggingUtil.uniqueWarn(log, "DbActions which contain mutated gene are removed that might need a further check") } From e603a3092b37323914c177801d18429422057a97 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 14:16:17 +0200 Subject: [PATCH 04/12] introducing probNamedExamples and PhenotypeDormantGene --- .../kotlin/org/evomaster/core/EMConfig.kt | 5 + .../core/problem/rest/data/RestCallAction.kt | 8 -- .../core/search/action/MainAction.kt | 38 +++++++ .../org/evomaster/core/search/gene/Gene.kt | 33 ++++++- .../core/search/gene/collection/EnumGene.kt | 11 +++ .../gene/interfaces/PhenotypeDormantGene.kt | 14 +++ .../gene/interfaces/UserExamplesGene.kt | 2 + .../gene/regex/DisjunctionListRxGene.kt | 99 +++++++++++++------ .../core/search/gene/wrapper/ChoiceGene.kt | 24 ++++- .../core/search/gene/wrapper/NullableGene.kt | 11 +-- .../core/search/gene/wrapper/OptionalGene.kt | 10 +- .../gene/wrapper/SelectableWrapperGene.kt | 25 ++++- .../evomaster/core/search/service/Sampler.kt | 29 ++++++ .../search/service/mutator/StandardMutator.kt | 6 +- 14 files changed, 253 insertions(+), 62 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/PhenotypeDormantGene.kt diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 0c2579b637..484bf62b83 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2824,6 +2824,11 @@ class EMConfig { @Probability(true) var probRestExamples = 0.20 + @Cfg("If any action contains any named example, make sure, with a given probability, that ALL fields for that example" + + " are using the provided values by the user") + @Probability(false) + var probNamedExamples = 0.50 + @Cfg("In REST, enable the supports of 'links' between resources defined in the OpenAPI schema, if any." + " When sampling a test case, if the last call has links, given this probability new calls are" + " added for the link.") diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt index 3b0803e9e7..61e865bea9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt @@ -454,12 +454,4 @@ class RestCallAction( return copy } - fun getNamedExamples() : Map { - return seeAllGenes() - .filterIsInstance() - .filter { it.isUsedForExamples() } - .flatMap { it.getAvailableExampleNames() } - .groupingBy { it } - .eachCount() - } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/action/MainAction.kt b/core/src/main/kotlin/org/evomaster/core/search/action/MainAction.kt index 858fe6c13b..e4faf52d98 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/action/MainAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/MainAction.kt @@ -2,6 +2,8 @@ package org.evomaster.core.search.action import org.evomaster.core.search.Individual import org.evomaster.core.search.StructuralElement +import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.UserExamplesGene abstract class MainAction( /** @@ -74,4 +76,40 @@ abstract class MainAction( return all.subList(index+1,all.size) } } + + + /** + * Return the name of all possible users examples in the chromosome, reporting their occurrences + */ + fun getNamedExamples() : Map { + return seeAllGenes() + .filterIsInstance() + .filter { it.isUsedForExamples() } + .flatMap { it.getAvailableExampleNames() } + .groupingBy { it } + .eachCount() + } + + fun getNamedExamplesInUse() : Map{ + return seeAllGenes() + .filter{ it.staticCheckIfImpactPhenotype()} + .filterIsInstance() + .filter { it.isUsedForExamples()} + .mapNotNull { it.getValueName() } + .groupingBy { it } + .eachCount() + } + + fun enforceNamedExample(name : String){ + + seeAllGenes() + .filterIsInstance() + .filter { it.isUsedForExamples() } + .filter { it.getAvailableExampleNames().contains(name) } + .forEach { + it.selectExampleByName(name) + (it as Gene).awakeGene() + } + + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt index 77cc663395..0bba610750 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt @@ -15,6 +15,7 @@ import org.evomaster.core.Lazy import org.evomaster.core.problem.enterprise.EnterpriseIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.RootElement +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.interfaces.TaintableGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.gene.interfaces.WrapperGene @@ -1253,10 +1254,40 @@ abstract class Gene( return p.staticCheckIfImpactPhenotype() } - open fun isChildUsed(child: Gene): Boolean { + /** + * Try to make sure the gene will impact the phenotype + */ + fun awakeGene(){ + + if (parent == null || parent !is Gene) { + //top genes are always impacting, as used directly + return + } + + val p = parent as Gene + + if(p is PhenotypeDormantGene && ! p.isChildActive(this)){ + val activated = p.tryToActivateGene(this) + if(!activated) { + //if we cannot activate it, there is nothing we can do, + //and we can just stop the recursion + return + } + } + + p.awakeGene() + } + + fun isChildUsed(child: Gene): Boolean { verifyChild(child) + //in most cases, it would be true. //only for few special genes this function would be overridden + + if(this is PhenotypeDormantGene){ + return isChildActive(child) + } + return true } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt index 217e559ca1..0ffafb234d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt @@ -206,6 +206,17 @@ class EnumGene>( return valueNames?.mapNotNull { it }?.toSet() ?: setOf() } + override fun selectExampleByName(name: String) { + if(!isUsedForExamples()){ + throw IllegalStateException("Selected enum does not contain example values") + } + if(valueNames == null || ! valueNames.contains(name)){ + throw IllegalArgumentException("Selected example value enum does not contain $name") + } + + index = valueNames.indexOf(name) + } + /** * [EnumGene] can be used in DTOs when the API spec contains either example values or an enum. * Since the [EnumGene] uses generics to hold values, this function returns the type to be used diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/PhenotypeDormantGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/PhenotypeDormantGene.kt new file mode 100644 index 0000000000..1b2eed7103 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/PhenotypeDormantGene.kt @@ -0,0 +1,14 @@ +package org.evomaster.core.search.gene.interfaces + +import org.evomaster.core.search.gene.Gene + + +/** + * A gene that might have internal genes that might not impact the phenotype, based on current status of this gene + */ +interface PhenotypeDormantGene { + + fun isChildActive(child: Gene): Boolean + + fun tryToActivateGene(child: Gene): Boolean +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt index d2ce3d8f98..3a37adcb77 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/interfaces/UserExamplesGene.kt @@ -33,5 +33,7 @@ interface UserExamplesGene { fun getValueName(): String? fun getAvailableExampleNames() : Set + + fun selectExampleByName(name: String) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/regex/DisjunctionListRxGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/regex/DisjunctionListRxGene.kt index 7b28618be8..43676bcfb4 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/regex/DisjunctionListRxGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/regex/DisjunctionListRxGene.kt @@ -4,6 +4,7 @@ import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.OutputFormat import org.evomaster.core.search.gene.root.CompositeFixedGene import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.impact.impactinfocollection.regex.DisjunctionListRxGeneImpact import org.evomaster.core.search.service.AdaptiveParameterControl @@ -17,20 +18,21 @@ import org.slf4j.LoggerFactory class DisjunctionListRxGene( val disjunctions: List -) : RxAtom, CompositeFixedGene("disjunction_list", disjunctions) { +) : RxAtom, CompositeFixedGene("disjunction_list", disjunctions), PhenotypeDormantGene { //FIXME refactor with ChoiceGene var activeDisjunction: Int = 0 - companion object{ + companion object { private const val PROB_NEXT = 0.1 private val log: Logger = LoggerFactory.getLogger(DisjunctionListRxGene::class.java) } - override fun checkForLocallyValidIgnoringChildren() : Boolean{ + override fun checkForLocallyValidIgnoringChildren(): Boolean { return activeDisjunction >= 0 && activeDisjunction < disjunctions.size } + override fun copyContent(): Gene { val copy = DisjunctionListRxGene(disjunctions.map { it.copy() as DisjunctionRxGene }) copy.activeDisjunction = this.activeDisjunction @@ -48,14 +50,14 @@ class DisjunctionListRxGene( randomize content of all disjunctions (since standardMutation can be invoked on another term) */ - disjunctions.forEach { it.randomize(randomness,tryToForceNewValue) } + disjunctions.forEach { it.randomize(randomness, tryToForceNewValue) } /** * randomly choose a new disjunction term */ if (disjunctions.size > 1) { log.trace("random disjunctions of DisjunctionListRxGene") - activeDisjunction = randomness.nextInt(0, disjunctions.size-1) + activeDisjunction = randomness.nextInt(0, disjunctions.size - 1) } } @@ -66,8 +68,9 @@ class DisjunctionListRxGene( additionalGeneMutationInfo: AdditionalGeneMutationInfo? ): Boolean { - if(disjunctions.size > 1 - && (!disjunctions[activeDisjunction].isMutable() || randomness.nextBoolean(PROB_NEXT))){ + if (disjunctions.size > 1 + && (!disjunctions[activeDisjunction].isMutable() || randomness.nextBoolean(PROB_NEXT)) + ) { //activate the next disjunction return true } @@ -78,7 +81,12 @@ class DisjunctionListRxGene( return listOf(disjunctions[activeDisjunction]).filter { it.isMutable() } } - override fun adaptiveSelectSubsetToMutate(randomness: Randomness, internalGenes: List, mwc: MutationWeightControl, additionalGeneMutationInfo: AdditionalGeneMutationInfo): List> { + override fun adaptiveSelectSubsetToMutate( + randomness: Randomness, + internalGenes: List, + mwc: MutationWeightControl, + additionalGeneMutationInfo: AdditionalGeneMutationInfo + ): List> { if (additionalGeneMutationInfo.impact == null || additionalGeneMutationInfo.impact !is DisjunctionListRxGeneImpact) throw IllegalArgumentException("mismatched gene impact") @@ -90,36 +98,49 @@ class DisjunctionListRxGene( } val selected = mwc.selectSubGene( - candidateGenesToMutate = internalGenes, - impacts = impacts, - targets = additionalGeneMutationInfo.targets, - forceNotEmpty = true, - adaptiveWeight = true + candidateGenesToMutate = internalGenes, + impacts = impacts, + targets = additionalGeneMutationInfo.targets, + forceNotEmpty = true, + adaptiveWeight = true ) - return selected.map { it to additionalGeneMutationInfo.copyFoInnerGene(additionalGeneMutationInfo.impact.disjunctions[disjunctions.indexOf(it)], it) }.toList() + return selected.map { + it to additionalGeneMutationInfo.copyFoInnerGene( + additionalGeneMutationInfo.impact.disjunctions[disjunctions.indexOf( + it + )], it + ) + }.toList() } - override fun shallowMutate(randomness: Randomness, apc: AdaptiveParameterControl, mwc: MutationWeightControl, selectionStrategy: SubsetGeneMutationSelectionStrategy, enableAdaptiveGeneMutation: Boolean, additionalGeneMutationInfo: AdditionalGeneMutationInfo?): Boolean { + override fun shallowMutate( + randomness: Randomness, + apc: AdaptiveParameterControl, + mwc: MutationWeightControl, + selectionStrategy: SubsetGeneMutationSelectionStrategy, + enableAdaptiveGeneMutation: Boolean, + additionalGeneMutationInfo: AdditionalGeneMutationInfo? + ): Boolean { // select another disjunction based on impact - if (enableAdaptiveGeneMutation || selectionStrategy == SubsetGeneMutationSelectionStrategy.ADAPTIVE_WEIGHT){ - additionalGeneMutationInfo?:throw IllegalStateException("") - if (additionalGeneMutationInfo.impact != null && additionalGeneMutationInfo.impact is DisjunctionListRxGeneImpact){ - val candidates = disjunctions.filterIndexed { index, _ -> index != activeDisjunction } + if (enableAdaptiveGeneMutation || selectionStrategy == SubsetGeneMutationSelectionStrategy.ADAPTIVE_WEIGHT) { + additionalGeneMutationInfo ?: throw IllegalStateException("") + if (additionalGeneMutationInfo.impact != null && additionalGeneMutationInfo.impact is DisjunctionListRxGeneImpact) { + val candidates = disjunctions.filterIndexed { index, _ -> index != activeDisjunction } val impacts = candidates.map { additionalGeneMutationInfo.impact.disjunctions[disjunctions.indexOf(it)] } val selected = mwc.selectSubGene( - candidateGenesToMutate = candidates, - impacts = impacts, - targets = additionalGeneMutationInfo.targets, - forceNotEmpty = true, - adaptiveWeight = true + candidateGenesToMutate = candidates, + impacts = impacts, + targets = additionalGeneMutationInfo.targets, + forceNotEmpty = true, + adaptiveWeight = true ) activeDisjunction = disjunctions.indexOf(randomness.choose(selected)) return true } - //throw IllegalArgumentException("mismatched gene impact") + //throw IllegalArgumentException("mismatched gene impact") } //activate the next disjunction @@ -127,12 +148,17 @@ class DisjunctionListRxGene( return true } - override fun getValueAsPrintableString(previousGenes: List, mode: GeneUtils.EscapeMode?, targetFormat: OutputFormat?, extraCheck: Boolean): String { + override fun getValueAsPrintableString( + previousGenes: List, + mode: GeneUtils.EscapeMode?, + targetFormat: OutputFormat?, + extraCheck: Boolean + ): String { if (disjunctions.isEmpty()) { return "" } return disjunctions[activeDisjunction] - .getValueAsPrintableString(previousGenes, mode, targetFormat) + .getValueAsPrintableString(previousGenes, mode, targetFormat) } @@ -146,14 +172,15 @@ class DisjunctionListRxGene( } return this.disjunctions[activeDisjunction] - .containsSameValueAs(other.disjunctions[activeDisjunction]) + .containsSameValueAs(other.disjunctions[activeDisjunction]) } override fun mutationWeight(): Double = disjunctions.map { it.mutationWeight() }.sum() + 1 override fun unsafeCopyValueFrom(other: Gene): Boolean { if (other !is DisjunctionListRxGene - || other.disjunctions.size != disjunctions.size) { + || other.disjunctions.size != disjunctions.size + ) { return false } @@ -161,14 +188,22 @@ class DisjunctionListRxGene( for (i in 0 until disjunctions.size) { ok = ok && this.disjunctions[i].unsafeCopyValueFrom(other.disjunctions[i]) } - if (ok){ + if (ok) { this.activeDisjunction = other.activeDisjunction } return ok } - override fun isChildUsed(child: Gene) : Boolean { + override fun isChildActive(child: Gene): Boolean { verifyChild(child) return child == disjunctions[activeDisjunction] } -} + + override fun tryToActivateGene(child: Gene): Boolean { + verifyChild(child) + + activeDisjunction = disjunctions.indexOf(child) + + return true + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt index e8d8a5683b..596b69d96e 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/ChoiceGene.kt @@ -2,6 +2,7 @@ package org.evomaster.core.search.gene.wrapper import org.evomaster.core.output.OutputFormat import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.interfaces.UserExamplesGene import org.evomaster.core.search.gene.interfaces.WrapperGene import org.evomaster.core.search.gene.root.CompositeFixedGene @@ -34,7 +35,7 @@ class ChoiceGene( */ valueNames: List? = null, -) : CompositeFixedGene(name, geneChoices), UserExamplesGene, WrapperGene where T : Gene { +) : CompositeFixedGene(name, geneChoices), UserExamplesGene, PhenotypeDormantGene, WrapperGene where T : Gene { companion object { private val log: Logger = LoggerFactory.getLogger(ChoiceGene::class.java) @@ -186,6 +187,17 @@ class ChoiceGene( return valueNames?.mapNotNull { it }?.toSet() ?: setOf() } + override fun selectExampleByName(name: String) { + if(!isUsedForExamples()){ + throw IllegalStateException("Selected choice does not contain example values") + } + if(valueNames == null || ! valueNames.contains(name)){ + throw IllegalArgumentException("Selected example value choice does not contain $name") + } + + selectActiveGene(valueNames.indexOf(name)) + } + /** * Copies the value of the other gene. The other gene * does not have to be [ChoiceGene]. @@ -290,8 +302,16 @@ class ChoiceGene( override fun isPrintable() = this.geneChoices[activeGeneIndex].isPrintable() - override fun isChildUsed(child: Gene) : Boolean{ + override fun isChildActive(child: Gene) : Boolean{ verifyChild(child) return child == geneChoices[activeGeneIndex] } + + override fun tryToActivateGene(child: Gene): Boolean { + verifyChild(child) + + activeGeneIndex = geneChoices.indexOf(child) + + return true + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/NullableGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/NullableGene.kt index ad08ff17f8..3a9db92713 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/NullableGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/NullableGene.kt @@ -3,6 +3,7 @@ package org.evomaster.core.search.gene.wrapper import org.evomaster.core.output.OutputFormat import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.interfaces.WrapperGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.impact.impactinfocollection.sql.NullableImpact @@ -17,7 +18,7 @@ class NullableGene(name: String, gene: Gene, isActive: Boolean = true, var nullLabel: String = "null" -) : SelectableWrapperGene(name, gene, isActive), WrapperGene { +) : SelectableWrapperGene(name, gene, isActive), WrapperGene, PhenotypeDormantGene { @@ -126,12 +127,4 @@ class NullableGene(name: String, && this.nullLabel == other.nullLabel } - - - - - override fun isChildUsed(child: Gene) : Boolean { - verifyChild(child) - return isActive - } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/OptionalGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/OptionalGene.kt index ad6f22a753..bfddef59db 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/OptionalGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/OptionalGene.kt @@ -3,6 +3,7 @@ package org.evomaster.core.search.gene.wrapper import org.evomaster.core.output.OutputFormat import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.interfaces.WrapperGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.impact.impactinfocollection.value.OptionalGeneImpact @@ -34,7 +35,7 @@ class OptionalGene(name: String, isActive: Boolean = true, var requestSelection: Boolean = false, var searchPercentageActive: Double = 1.0 -) : SelectableWrapperGene(name, gene, isActive), WrapperGene { +) : SelectableWrapperGene(name, gene, isActive), WrapperGene, PhenotypeDormantGene { companion object{ @@ -156,11 +157,4 @@ class OptionalGene(name: String, return gene.getValueAsRawString() } - - - - override fun isChildUsed(child: Gene) : Boolean { - verifyChild(child) - return isActive - } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/SelectableWrapperGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/SelectableWrapperGene.kt index a5534bdaf8..96ff0aa68a 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/SelectableWrapperGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/wrapper/SelectableWrapperGene.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search.gene.wrapper import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.interfaces.PhenotypeDormantGene import org.evomaster.core.search.gene.interfaces.WrapperGene import org.evomaster.core.search.gene.root.CompositeFixedGene import org.evomaster.core.search.service.AdaptiveParameterControl @@ -17,7 +18,7 @@ import org.evomaster.core.search.service.mutator.genemutation.SubsetGeneMutation abstract class SelectableWrapperGene(name: String, val gene: Gene, var isActive: Boolean = true -): CompositeFixedGene(name, gene), WrapperGene { +): CompositeFixedGene(name, gene), WrapperGene, PhenotypeDormantGene { /** @@ -112,4 +113,26 @@ abstract class SelectableWrapperGene(name: String, */ return !isActive || gene.isPrintable() } + + override fun isChildActive(child: Gene) : Boolean { + verifyChild(child) + return isActive + } + + override fun tryToActivateGene(child: Gene): Boolean { + verifyChild(child) + + if(isActive){ + //nothing to do + return true + } + + if(!selectable){ + //can't activate it + return false + } else { + isActive = true + return true + } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index 82645396f8..5e6dc6fdf4 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -6,6 +6,7 @@ import org.evomaster.core.EMConfig import org.evomaster.core.search.action.Action import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual +import org.evomaster.core.search.action.MainAction import org.evomaster.core.search.gene.wrapper.OptionalGene import org.evomaster.core.search.tracer.TrackOperator import org.slf4j.Logger @@ -132,11 +133,39 @@ abstract class Sampler : TrackOperator where T : Individual { .filter { it.selectable } .forEach { it.isActive = on } } + + if(a is MainAction && randomness.nextBoolean(config.probNamedExamples)){ + applyNamedExamples(a) + } } applyDerivedParamModifications(ind) } + private fun applyNamedExamples(action: MainAction) { + + val inUse = action.getNamedExamplesInUse() + if (inUse.isEmpty()) { + return + } + + val candidates = action.getNamedExamples() + //we skip named examples that are used only once + .filter { it.value >= 2 } + + if (inUse.any { it.value == candidates[it.key] }) { + //one example is already fully covered + return + } + + //we look at something already selected, with more than 1 occurrence, and not fully used yet + val options = inUse.filter { candidates.contains(it.key) } + + val choice = randomness.choose(options.keys) + + action.enforceNamedExample(choice) + } + open fun applyDerivedParamModifications(ind: T){ // needs to be overridden } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt index 2c2cf7ef83..e1824a43e9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt @@ -317,7 +317,11 @@ open class StandardMutator : Mutator() where T : Individual { * are done to maintain and satisfy validity */ private fun mutationPostProcessing(individual: T) { - sampler + + //right now, we do nothing here... + //originally, though to handle probNamedExamples here, but that would be wrong, + //as after few mutations on same individual will be guaranteed to use only examples... + //so we do that at Sampling Time only } From 1d19e9704d46aa50b279eaea3d2db11d720bdbe2 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 14:40:54 +0200 Subject: [PATCH 05/12] fixed test --- .../problem/rest/RestActionBuilderV3Test.kt | 35 +++++++++++++++++-- docs/options.md | 1 + 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt index fe8a9f66e8..a87e96af17 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt @@ -2255,7 +2255,38 @@ class RestActionBuilderV3Test{ assertEquals(3, examples["Bar"]) assertEquals(1, examples["Hello"]) - - //TODO apply and check examples + post.enforceNamedExample("Foo") + val withFoo = post.parameters.joinToString("\n"){it.name + ": " +it.primaryGene().getValueAsRawString()} + assertTrue(withFoo.contains("123"), withFoo) + assertTrue(withFoo.contains("nameF"), withFoo) + assertTrue(withFoo.contains("77"), withFoo) + assertTrue(withFoo.contains("xfoo"), withFoo) + + post.enforceNamedExample("Bar") + val withBar = post.parameters.joinToString("\n"){it.name + ": " +it.primaryGene().getValueAsRawString()} + assertTrue(withBar.contains("42"), withBar) + assertTrue(withBar.contains("nameB"), withBar) + assertTrue(withBar.contains("xbar"), withBar) + assertTrue(withBar.contains("ybar"), withBar) + // "extra" for Foo should still be there unchanged. + // actually, that is not true... that only apply to distinct trees... + // all object examples for the same field would be separate branches in a ChoiceGene + //assertTrue(withBar.contains("77"), withBar) + + post.enforceNamedExample("Hello") + val withHello = post.parameters.joinToString("\n"){it.name + ": " +it.primaryGene().getValueAsRawString()} + //only "id" should be modified + assertTrue(withHello.contains("667"), withHello) + //rest should stay in other fields, but not the body object +// assertTrue(withHello.contains("nameB"), withHello) +// assertTrue(withHello.contains("77"), withHello) + assertTrue(withHello.contains("xbar"), withHello) + assertTrue(withHello.contains("ybar"), withHello) + + post.enforceNamedExample("Foo") + val withFooAgain = post.parameters.joinToString("\n"){it.name + ": " +it.primaryGene().getValueAsRawString()} + // "x" modified, but not "y" + assertTrue(withFooAgain.contains("xfoo"), withFooAgain) + assertTrue(withFooAgain.contains("ybar"), withFooAgain) } } diff --git a/docs/options.md b/docs/options.md index f9a13d5c5a..6736e0d71c 100644 --- a/docs/options.md +++ b/docs/options.md @@ -178,6 +178,7 @@ There are 3 types of options: |`outputExecutedSQL`| __Enum__. Whether to output executed sql info. *DEBUG option*. *Valid values*: `NONE, ALL_AT_END, ONCE_EXECUTED`. *Default value*: `NONE`.| |`overrideAuthExternalEndpointURL`| __String__. Override the value of externalEndpointURL in auth configurations. This is useful when the auth server is running locally on an ephemeral port, or when several instances are run in parallel, to avoid creating/modifying auth configuration files. If what provided is a URL starting with 'http', then full replacement will occur. Otherwise, the input will be treated as a 'hostname:port', and only that info will be updated (e.g., path element of the URL will not change). *Default value*: `null`.| |`populationSize`| __Int__. Define the population size in the search algorithms that use populations (e.g., Genetic Algorithms, but not MIO). *Constraints*: `min=1.0`. *Default value*: `30`.| +|`probNamedExamples`| __Double__. If any action contains any named example, make sure, with a given probability, that ALL fields for that example are using the provided values by the user. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| |`probOfApplySQLActionToCreateResources`| __Double__. Specify a probability to apply SQL actions for preparing resources for REST Action. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.1`.| |`probOfArchiveMutation`| __Double__. Specify a probability to enable archive-based mutation. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| |`probOfEnablingResourceDependencyHeuristics`| __Double__. Specify whether to enable resource dependency heuristics, i.e, probOfEnablingResourceDependencyHeuristics > 0.0. Note that the option is available to be enabled only if resource-based smart sampling is enable. This option has an effect on sampling multiple resources and mutating a structure of an individual. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.95`.| From 63053ce781c4858a8c55c71ca7047b9e6183e689 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 15:41:43 +0200 Subject: [PATCH 06/12] adding NAMED_EXAMPLE as new BB target in fitness function --- .../service/fitness/AbstractRestFitness.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt index 1e0b9621ad..cb60593d4a 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt @@ -503,7 +503,7 @@ abstract class AbstractRestFitness : HttpWsFitness() { fv.coverTarget(idMapper.handleLocalTarget("RESPONSE_BODY_PAYLOAD_${call.id}_${result.getBodyType()}")) /* - explicit targets for examples + explicit targets for single example entries */ val examples = call.seeAllGenes() .filter { it is UserExamplesGene && it.isUsedForExamples() } @@ -524,6 +524,26 @@ abstract class AbstractRestFitness : HttpWsFitness() { val target = "EXAMPLE_${call.id}_${name}_$label" fv.coverTarget(idMapper.handleLocalTarget(target)) } + + /* + explicit targets for named examples, but only when fully used + */ + val allExampleNames = call.getNamedExamples() + val exampleNamesInUse = call.getNamedExamplesInUse() + + for(e in exampleNamesInUse){ + /* + TODO this would not consider possible special cases (eg when have anyOf constraints) in which + same example name could appear in different subtrees of same ChoiceGene... + bit tricky to handle... not sure if really a good ROI in trying to handle it now... + */ + if(e.value != allExampleNames[e.key]){ + continue + } + + val target = "NAMED_EXAMPLE_${call.id}_${e.key}" + fv.coverTarget(idMapper.handleLocalTarget(target)) + } } private fun handleAdditionalStatusTargetDescription( From c8121ca642c27b2dd909ac6273bd13a6a9bdd76f Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 20:59:43 +0200 Subject: [PATCH 07/12] starting with E2E --- .../namedexample/NamedExampleApplication.kt | 42 +++++++++++++++++++ .../v3/namedexample/NamedExampleDto.kt | 6 +++ 2 files changed, 48 insertions(+) create mode 100644 core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt create mode 100644 core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleDto.kt diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt new file mode 100644 index 0000000000..063307f841 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt @@ -0,0 +1,42 @@ +package com.foo.rest.examples.spring.openapi.v3.namedexample + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import javax.ws.rs.QueryParam + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/namedexample"]) +@RestController +open class NamedExampleApplication { + + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(NamedExampleApplication::class.java, *args) + } + } + + + @GetMapping + open fun get( + @QueryParam("q0") q0: String?, + @QueryParam("q1") q1: String?, + @QueryParam("q2") q2: String?, + @QueryParam("q3") q3: String?, + @QueryParam("q4") q4: String?, + @RequestBody dto: NamedExampleDto + ) : ResponseEntity { + + //on purpose do nothing with input... should still be selected in test suites due to BB coverage + + return ResponseEntity.ok("Hello World!!!") + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleDto.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleDto.kt new file mode 100644 index 0000000000..9634d5fa58 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleDto.kt @@ -0,0 +1,6 @@ +package com.foo.rest.examples.spring.openapi.v3.namedexample + +class NamedExampleDto { + + var data : String? = null +} \ No newline at end of file From 6d44ba3f5d6919fe696abeea767885cb1d809552 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 21:07:45 +0200 Subject: [PATCH 08/12] fixed missing edge case --- .../main/kotlin/org/evomaster/core/search/service/Sampler.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index 5e6dc6fdf4..a90f74ead4 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -152,6 +152,9 @@ abstract class Sampler : TrackOperator where T : Individual { val candidates = action.getNamedExamples() //we skip named examples that are used only once .filter { it.value >= 2 } + if(candidates.isEmpty()){ + return + } if (inUse.any { it.value == candidates[it.key] }) { //one example is already fully covered @@ -160,6 +163,7 @@ abstract class Sampler : TrackOperator where T : Individual { //we look at something already selected, with more than 1 occurrence, and not fully used yet val options = inUse.filter { candidates.contains(it.key) } + assert(options.isNotEmpty()) val choice = randomness.choose(options.keys) From 5c36fcc50e7fd3f5764c38268b58db7cf0ed0087 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 13 Apr 2026 21:52:08 +0200 Subject: [PATCH 09/12] setting up E2E --- .../namedexample/NamedExampleApplication.kt | 5 +- .../static/openapi-named-example.yaml | 142 ++++++++++++++++++ .../v3/namedexample/NamedExampleController.kt | 16 ++ .../v3/namedexample/NamedExampleEMTest.kt | 36 +++++ .../core/search/gene/collection/EnumGene.kt | 13 +- 5 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-named-example.yaml create mode 100644 core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleController.kt create mode 100644 core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt index 063307f841..9f8a84c1a9 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt @@ -5,6 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @@ -25,8 +26,8 @@ open class NamedExampleApplication { } - @GetMapping - open fun get( + @PostMapping + open fun post( @QueryParam("q0") q0: String?, @QueryParam("q1") q1: String?, @QueryParam("q2") q2: String?, diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-named-example.yaml b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-named-example.yaml new file mode 100644 index 0000000000..0cebe25d57 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-named-example.yaml @@ -0,0 +1,142 @@ +openapi: 3.1.0 +info: + title: OpenAPI definition + version: v0 +servers: + - url: http://localhost:8080 + description: Generated server url +paths: + /api/namedexample: + post: + tags: + - named-example-application + operationId: post + parameters: + - name: q0 + in: query + required: false + schema: + type: string + examples: + foo: + value: "fooq0" + - name: q1 + in: query + required: false + schema: + type: string + examples: + foo: + value: "fooq1" + a0: + value: "X" + a1: + value: "X" + a2: + value: "X" + a3: + value: "X" + a4: + value: "X" + a5: + value: "X" + a6: + value: "X" + a7: + value: "X" + a8: + value: "X" + a9: + value: "X" + - name: q2 + in: query + required: false + schema: + type: string + examples: + foo: + value: "fooq2" + b0: + value: "X" + b1: + value: "X" + b2: + value: "X" + b3: + value: "X" + b4: + value: "X" + b5: + value: "X" + b6: + value: "X" + b7: + value: "X" + b8: + value: "X" + b9: + value: "X" + - name: q3 + in: query + required: false + schema: + type: string + examples: + foo: + value: "fooq3" + c0: + value: "X" + c1: + value: "X" + c2: + value: "X" + c3: + value: "X" + c4: + value: "X" + c5: + value: "X" + c6: + value: "X" + c7: + value: "X" + c8: + value: "X" + c9: + value: "X" + - name: q4 + in: query + required: false + schema: + type: string + examples: + foo: + value: "fooq4" + a0: + value: "X" + a1: + value: "X" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NamedExampleDto' + examples: + foo: + value: + data: "fooBody" + responses: + '200': + description: OK + content: + '*/*': + schema: + type: string +components: + schemas: + NamedExampleDto: + type: object + properties: + data: + type: string \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleController.kt new file mode 100644 index 0000000000..b7ee4c445d --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleController.kt @@ -0,0 +1,16 @@ +package com.foo.rest.examples.spring.openapi.v3.namedexample + +import com.foo.rest.examples.spring.openapi.v3.SpringController +import org.evomaster.client.java.controller.problem.ProblemInfo +import org.evomaster.client.java.controller.problem.RestProblem + +class NamedExampleController : SpringController(NamedExampleApplication::class.java){ + + + override fun getProblemInfo(): ProblemInfo { + return RestProblem( + "http://localhost:$sutPort/openapi-named-example.yaml", + null + ) + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt new file mode 100644 index 0000000000..da1fe99a48 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt @@ -0,0 +1,36 @@ +package org.evomaster.e2etests.spring.openapi.v3.namedexample + +import com.foo.rest.examples.spring.openapi.v3.namedexample.NamedExampleController +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class NamedExampleEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(NamedExampleController()) + } + } + + @Test + fun testRunEM() { + runTestHandlingFlakyAndCompilation( + "NamedExampleEM", + 200, + ) { args: MutableList -> + + val solution = initAndRun(args) + + Assertions.assertTrue(solution.individuals.size >= 1) + assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/object", "OK") + + TODO add checks on named example target + } + } + +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt index 0ffafb234d..145073d013 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt @@ -70,8 +70,17 @@ class EnumGene>( log.warn("Enum Gene (name: $name) has empty list of values") values = listOf() }else{ - val list = data - .toSet() // we want no duplicate + + val elements = if(valueNames == null){ + // we want no duplicate + data.toSet() + } else { + //if we have named value, then we must not use a set, as there might be duplicates, and + //we would need to know which names map to which duplicated value + data + } + + val list = elements .toList() // need ordering to specify index of selection, so Set would not do .sorted() // sort, to make meaningful list comparisons .map { if (it is String) it.intern() as T else it } //if strings, make sure to intern them From 28e9e9a34cb6588510ff91da9b4e2b7cee85c0d9 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 14 Apr 2026 10:24:35 +0200 Subject: [PATCH 10/12] increased budget to unstable E2E tests --- .../e2etests/emb/json/genome/GenomeNexusExampleEMTest.java | 2 +- .../emb/json/proxyprint/ProxyPrintExampleEMTest.java | 4 ++-- .../spring/rest/mongo/findoneby/MongoFindOneByEMTest.java | 5 +++-- .../e2etests/spring/rest/postgres/json/JsonColumnEMTest.kt | 4 ++-- .../evomaster/e2etests/spring/examples/json/JsonEMTest.java | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/genome/GenomeNexusExampleEMTest.java b/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/genome/GenomeNexusExampleEMTest.java index 04b49cd2f4..7fdc2a8566 100644 --- a/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/genome/GenomeNexusExampleEMTest.java +++ b/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/genome/GenomeNexusExampleEMTest.java @@ -24,7 +24,7 @@ public void runEMTest() throws Throwable { runTestHandlingFlakyAndCompilation( "GenomeNexusExampleGeneratedEMTest", "org.bar.GenomeNexusExampleGeneratedEMTest", - 500, + 2000, true, (args) -> { Solution solution = initAndRun(args); diff --git a/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/proxyprint/ProxyPrintExampleEMTest.java b/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/proxyprint/ProxyPrintExampleEMTest.java index 5bf34762bf..38453129f5 100644 --- a/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/proxyprint/ProxyPrintExampleEMTest.java +++ b/core-tests/e2e-tests/spring/emb-json/src/test/java/org/evomaster/e2etests/emb/json/proxyprint/ProxyPrintExampleEMTest.java @@ -24,7 +24,7 @@ public void runEMTest() throws Throwable { runTestHandlingFlakyAndCompilation( "ProxyPrintExampleGeneratedEMTest", "org.foo.ProxyPrintExampleGeneratedEMTest", - 5_000, + 7_000, true, (args) -> { setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true"); @@ -34,7 +34,7 @@ public void runEMTest() throws Throwable { assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/api/json", "Printing"); }, - 3 + 6 ); } } diff --git a/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneby/MongoFindOneByEMTest.java b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneby/MongoFindOneByEMTest.java index a48d0366d1..2de6e2c631 100644 --- a/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneby/MongoFindOneByEMTest.java +++ b/core-tests/e2e-tests/spring/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/findoneby/MongoFindOneByEMTest.java @@ -34,7 +34,7 @@ public void testFindOneOnGivenEndpoint(String endpoint) throws Throwable { runTestHandlingFlaky( "MongoFindOneByEM_" + id, "org.foo.spring.rest.mongo.MongoFindOneByEM"+id, - 1000, + 1500, true, (args) -> { setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true"); @@ -54,6 +54,7 @@ public void testFindOneOnGivenEndpoint(String endpoint) throws Throwable { assertFalse(solution.getIndividuals().isEmpty()); assertHasAtLeastOne(solution, HttpVerb.GET, 400, endpoint, null); assertHasAtLeastOne(solution, HttpVerb.GET, 200, endpoint, null); - }); + }, + 6); } } diff --git a/core-tests/e2e-tests/spring/spring-rest-postgres/src/test/kotlin/org/evomaster/e2etests/spring/rest/postgres/json/JsonColumnEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-postgres/src/test/kotlin/org/evomaster/e2etests/spring/rest/postgres/json/JsonColumnEMTest.kt index a5952b3c89..638ebd6060 100644 --- a/core-tests/e2e-tests/spring/spring-rest-postgres/src/test/kotlin/org/evomaster/e2etests/spring/rest/postgres/json/JsonColumnEMTest.kt +++ b/core-tests/e2e-tests/spring/spring-rest-postgres/src/test/kotlin/org/evomaster/e2etests/spring/rest/postgres/json/JsonColumnEMTest.kt @@ -28,7 +28,7 @@ class JsonColumnEMTest : SpringRestPostgresTestBase(){ runTestHandlingFlakyAndCompilation( "JsonColumnEM", "org.bar.JsonColumnEM", - 10 + 100 ) { args -> val solution = initAndRun(args) @@ -47,4 +47,4 @@ class JsonColumnEMTest : SpringRestPostgresTestBase(){ }) } } -} \ No newline at end of file +} diff --git a/core-tests/jdk-8/spring-rest-openapi-v2-tests/src/test/java/org/evomaster/e2etests/spring/examples/json/JsonEMTest.java b/core-tests/jdk-8/spring-rest-openapi-v2-tests/src/test/java/org/evomaster/e2etests/spring/examples/json/JsonEMTest.java index add678f9dd..a2edfebe26 100644 --- a/core-tests/jdk-8/spring-rest-openapi-v2-tests/src/test/java/org/evomaster/e2etests/spring/examples/json/JsonEMTest.java +++ b/core-tests/jdk-8/spring-rest-openapi-v2-tests/src/test/java/org/evomaster/e2etests/spring/examples/json/JsonEMTest.java @@ -19,12 +19,12 @@ public static void initClass() throws Exception { } @Test - public void testRunEM() throws Throwable { + public void JsonEMTest_testRunEM() throws Throwable { runTestHandlingFlakyAndCompilation( "JsonEMTest", - 5_000, - 5, + 10_000, + 6, (args) -> { setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true"); From 52b87a01ae9d5959e774ffc5d0b0ddf830cf7ac5 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 14 Apr 2026 13:31:51 +0200 Subject: [PATCH 11/12] completed E2E for named examples --- .../v3/namedexample/NamedExampleApplication.kt | 4 ++-- .../openapi/v3/namedexample/NamedExampleEMTest.kt | 12 +++++++++--- .../evomaster/core/mutatortest/OneMaxMutator.kt | 4 ++-- .../evomaster/core/output/TestSuiteOrganizer.kt | 2 +- .../core/output/service/TestSuiteWriter.kt | 2 +- .../rest/service/fitness/AbstractRestFitness.kt | 2 +- .../org/evomaster/core/search/FitnessValue.kt | 14 ++++++++++---- .../core/search/gene/collection/EnumGene.kt | 3 ++- .../org/evomaster/core/search/service/IdMapper.kt | 13 ++++++++++++- .../org/evomaster/core/search/service/Sampler.kt | 6 +++++- .../org/evomaster/core/search/FitnessValueTest.kt | 4 ++-- .../core/search/service/ProcessMonitorTest.kt | 2 +- 12 files changed, 48 insertions(+), 20 deletions(-) diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt index 9f8a84c1a9..e8677c3c5a 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/namedexample/NamedExampleApplication.kt @@ -38,6 +38,6 @@ open class NamedExampleApplication { //on purpose do nothing with input... should still be selected in test suites due to BB coverage - return ResponseEntity.ok("Hello World!!!") + return ResponseEntity.ok("OK") } -} \ No newline at end of file +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt index da1fe99a48..529b4c0f2f 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/namedexample/NamedExampleEMTest.kt @@ -1,7 +1,9 @@ package org.evomaster.e2etests.spring.openapi.v3.namedexample import com.foo.rest.examples.spring.openapi.v3.namedexample.NamedExampleController +import junit.framework.TestCase.assertTrue import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.core.search.service.IdMapper import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeAll @@ -24,12 +26,16 @@ class NamedExampleEMTest : SpringTestBase(){ 200, ) { args: MutableList -> - val solution = initAndRun(args) + val (injector, solution) = initAndDebug(args) Assertions.assertTrue(solution.individuals.size >= 1) - assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/object", "OK") + assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/api/namedexample", "OK") - TODO add checks on named example target + val fv = solution.overall + + val idMapper = injector.getInstance(IdMapper::class.java) + val covered = fv.coveredTargets().any{ IdMapper.isNamedExample(idMapper.getDescriptiveId(it))} + Assertions.assertTrue(covered) } } diff --git a/core-tests/integration-tests/core-it/src/test/kotlin/org/evomaster/core/mutatortest/OneMaxMutator.kt b/core-tests/integration-tests/core-it/src/test/kotlin/org/evomaster/core/mutatortest/OneMaxMutator.kt index 25fafddee3..a64e098793 100644 --- a/core-tests/integration-tests/core-it/src/test/kotlin/org/evomaster/core/mutatortest/OneMaxMutator.kt +++ b/core-tests/integration-tests/core-it/src/test/kotlin/org/evomaster/core/mutatortest/OneMaxMutator.kt @@ -105,7 +105,7 @@ class OneMaxMutator { m.close() val tp = reportFP(config.mutatedGeneFile, improve) - val covered = solution.overall.coveredTargets() * 1.0 /n + val covered = solution.overall.numberOfCoveredTargets() * 1.0 /n val result = Pair(tp, covered) map.getOrPut(expId){ mutableListOf()}.add(result) @@ -125,4 +125,4 @@ class OneMaxMutator { fun main(array: Array){ OneMaxMutator().mutatorExp() -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/output/TestSuiteOrganizer.kt b/core/src/main/kotlin/org/evomaster/core/output/TestSuiteOrganizer.kt index 98be50c28c..0fd305be57 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/TestSuiteOrganizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/TestSuiteOrganizer.kt @@ -191,7 +191,7 @@ class SortingHelper { * The purpose is to give an example of sorting based on fitness information. */ private val coveredTargets: Comparator> = compareBy { - it.fitness.coveredTargets() + it.fitness.numberOfCoveredTargets() } /** diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 7219c50e7b..b304bda574 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -338,7 +338,7 @@ class TestSuiteWriter { lines.addBlockCommentLine(" The generated test suite contains ${solution.individuals.size} tests") classDescriptionEmptyLine(lines) - lines.addBlockCommentLine(" Covered targets: ${solution.overall.coveredTargets()}") + lines.addBlockCommentLine(" Covered targets: ${solution.overall.numberOfCoveredTargets()}") classDescriptionEmptyLine(lines) lines.addBlockCommentLine(" Used time: ${searchTimeController.getElapsedTime()}") classDescriptionEmptyLine(lines) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt index cb60593d4a..48b10e6e5e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt @@ -541,7 +541,7 @@ abstract class AbstractRestFitness : HttpWsFitness() { continue } - val target = "NAMED_EXAMPLE_${call.id}_${e.key}" + val target = idMapper.getNamedExampleId(call.id, e.key, status ?: 0) fv.coverTarget(idMapper.handleLocalTarget(target)) } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index b3c25961f9..aff1ff49da 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -1,7 +1,6 @@ package org.evomaster.core.search import com.webfuzzing.commons.faults.DefinedFaultCategory -import com.webfuzzing.commons.faults.FaultCategory import org.evomaster.client.java.controller.api.dto.BootTimeInfoDto import org.evomaster.client.java.controller.api.dto.database.execution.MongoFailedQuery import org.evomaster.core.EMConfig @@ -200,11 +199,18 @@ class FitnessValue( return targets.filterKeys { targetIds.contains(it)}.values.map { h -> h.score }.sum() } - fun coveredTargets(): Int { + fun coveredTargets() : Set { + return targets.entries + .filter { it.value.score == MAX_VALUE } + .map { it.key } + .toSet() + } + + fun numberOfCoveredTargets(): Int { return targets.values.count { t -> t.score == MAX_VALUE } } - fun coveredTargets(prefix: String, idMapper: IdMapper) : Int{ + fun numberOfCoveredTargets(prefix: String, idMapper: IdMapper) : Int{ return targets.entries .filter { it.value.score == MAX_VALUE } @@ -253,7 +259,7 @@ class FitnessValue( */ fun unionWithBootTimeCoveredTargets(prefix: String?, idMapper: IdMapper, bootTimeInfoDto: BootTimeInfoDto?): TargetStatistic{ if (bootTimeInfoDto?.targets == null){ - return (if (prefix == null) coveredTargets() else coveredTargets(prefix, idMapper)).run { + return (if (prefix == null) numberOfCoveredTargets() else numberOfCoveredTargets(prefix, idMapper)).run { TargetStatistic( bootTime = BOOT_TIME_INFO_UNAVAILABLE, searchTime = this - coveredTargetsDuringSeeding(), diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt index 145073d013..a1df632f05 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt @@ -260,7 +260,8 @@ class EnumGene>( override fun containsSameValueAs(other: Gene): Boolean { if (other !is EnumGene<*>) { - throw IllegalArgumentException("Invalid gene type ${other.javaClass}") + return false // FIXME + //throw IllegalArgumentException("Invalid gene type ${other.javaClass}") } //FIXME what if compared to another enum with different values??? return this.index == other.index diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/IdMapper.kt b/core/src/main/kotlin/org/evomaster/core/search/service/IdMapper.kt index d14465f386..b82ab83cf4 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/IdMapper.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/IdMapper.kt @@ -20,10 +20,15 @@ class IdMapper { private const val FAULT_OBJECTIVE_PREFIX = "PotentialFault" + private const val NAMED_EXAMPLE = "NamedExample" + /** * local objective prefix might depend on problems, eg, HTTP_SUCCESS and HTTP_FAULT for REST * it can be identified with its numeric id, ie, less than 0 * however we need this key to specify whether to consider such objectives in impact collections + * + * TODO is this verified anywhere? eg, i think we created many new local targets not using this... + * TODO if this is still relevant, should be enforced in an invariant somewhere */ const val LOCAL_OBJECTIVE_KEY = "Local" @@ -43,6 +48,8 @@ class IdMapper { private const val GQL_NO_ERRORS = "GQL_NO_ERRORS" + fun isNamedExample(descriptiveId: String) = descriptiveId.startsWith(NAMED_EXAMPLE) + fun isFault(descriptiveId: String) = descriptiveId.startsWith(FAULT_OBJECTIVE_PREFIX) fun isSpecifiedFault(descriptiveId: String, category: FaultCategory) = @@ -101,6 +108,10 @@ class IdMapper { return "$FAULT_OBJECTIVE_PREFIX ${category.label} $postfix" } + fun getNamedExampleId(actionId: String, exampleName: String, statusCode: Int) : String { + return "$NAMED_EXAMPLE $actionId - $exampleName - $statusCode" + } + fun getGQLNoErrors(method: String) = "$GQL_NO_ERRORS:$method" fun getHandledRPC(postfix: String): String { @@ -133,4 +144,4 @@ class IdMapper { fun isRPCHandledAndSuccess(id: Int) = mapping[id]?.let { isRPCHandledAndSuccess(it) } == true -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index a90f74ead4..22f50c1c82 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -163,7 +163,11 @@ abstract class Sampler : TrackOperator where T : Individual { //we look at something already selected, with more than 1 occurrence, and not fully used yet val options = inUse.filter { candidates.contains(it.key) } - assert(options.isNotEmpty()) + if(options.isEmpty()){ + //this can happen when there is a selected named example with a single occurrence in the schema. + //so, nothing to do + return + } val choice = randomness.choose(options.keys) diff --git a/core/src/test/kotlin/org/evomaster/core/search/FitnessValueTest.kt b/core/src/test/kotlin/org/evomaster/core/search/FitnessValueTest.kt index b7ce647f0f..f8e634b5c8 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/FitnessValueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/FitnessValueTest.kt @@ -67,7 +67,7 @@ class FitnessValueTest { fv.coverTarget(4) //line fv.coverTarget(-12) - assertEquals(6, fv.coveredTargets()) + assertEquals(6, fv.numberOfCoveredTargets()) var linesInfo = fv.unionWithBootTimeCoveredTargets(ObjectiveNaming.LINE, idMapper, null) assertEquals(3, linesInfo.total) @@ -123,4 +123,4 @@ class FitnessValueTest { assertEquals(3, linesInfo.searchTime) } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt index 58f28bfe7c..abd5f20f1d 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt @@ -157,7 +157,7 @@ class ProcessMonitorTest{ thus, currently, the serialized process data could only contain fitness info and impact info */ // assertEquals(individual.seeGenes().size, evalIndividual.individual.seeGenes().size) - assertEquals(evalIndividual.fitness.coveredTargets(), evalIndividual.fitness.coveredTargets()) + assertEquals(evalIndividual.fitness.numberOfCoveredTargets(), evalIndividual.fitness.numberOfCoveredTargets()) evalIndividual.fitness.getViewOfData().forEach { (t, u) -> assertEquals(evalIndividual.fitness.getHeuristic(t) , u.score) } From d28bba4735d54145bd186b0c5b05cbc7ec589037 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 14 Apr 2026 18:29:48 +0200 Subject: [PATCH 12/12] increased budget for unstable tests --- .../spring/openapi/v3/gson/mapdouble/MapDoubleGsonEMTest.kt | 2 +- .../openapi/v3/jackson/mapdouble/MapDoubleJacksonEMTest.kt | 2 +- .../openapi/v3/jackson/maplistint/MapListIntJacksonEMTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/gson/mapdouble/MapDoubleGsonEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/gson/mapdouble/MapDoubleGsonEMTest.kt index b576b47f1b..685c0d7995 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/gson/mapdouble/MapDoubleGsonEMTest.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/gson/mapdouble/MapDoubleGsonEMTest.kt @@ -21,7 +21,7 @@ class MapDoubleGsonEMTest : SpringTestBase() { fun basicEMTest() { runTestHandlingFlakyAndCompilation( "MapDoubleGsonEMTestGenerated", - 1_000 + 2_000 ) { args: List -> setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true") diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/mapdouble/MapDoubleJacksonEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/mapdouble/MapDoubleJacksonEMTest.kt index 95906ff0cf..933eb5ad13 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/mapdouble/MapDoubleJacksonEMTest.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/mapdouble/MapDoubleJacksonEMTest.kt @@ -21,7 +21,7 @@ class MapDoubleJacksonEMTest : SpringTestBase() { fun basicEMTest() { runTestHandlingFlakyAndCompilation( "MapDoubleJacksonEM", - 1000 + 2000 ) { args: List -> setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true") diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/maplistint/MapListIntJacksonEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/maplistint/MapListIntJacksonEMTest.kt index 3cfcef8825..2973dc5468 100644 --- a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/maplistint/MapListIntJacksonEMTest.kt +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/jackson/maplistint/MapListIntJacksonEMTest.kt @@ -21,7 +21,7 @@ class MapListIntJacksonEMTest : SpringTestBase() { fun basicEMTest() { runTestHandlingFlakyAndCompilation( "MapListIntJacksonEM", - 2000 + 3000 ) { args: List -> setOption(args, "taintForceSelectionOfGenesWithSpecialization", "true")