From e07430fd6859074e9547c581828800ec49c611aa Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Thu, 28 Nov 2024 21:11:44 -0300 Subject: [PATCH 01/15] Negative numbers and empty strings added to query param naming --- .../RestActionTestCaseNamingStrategy.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 652349ad01..cb8f48c074 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -12,6 +12,8 @@ import org.evomaster.core.search.Solution import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.EvaluatedAction import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.numeric.NumberGene +import org.evomaster.core.search.gene.string.StringGene import javax.ws.rs.core.MediaType open class RestActionTestCaseNamingStrategy( @@ -161,12 +163,38 @@ open class RestActionTestCaseNamingStrategy( result.add(and) } } + + val numberQueryParams = getNumberQueryParams(queryParams) + numberQueryParams.forEachIndexed { index, queryParam -> + result.add("negative") + result.add(queryParam.name) + if (index != numberQueryParams.lastIndex) { + result.add(and) + } + } + + val emptyStringQueryParams = getEmptyStringQueryParams(queryParams) + emptyStringQueryParams.forEachIndexed { index, queryParam -> + result.add("empty") + result.add(queryParam.name) + if (index != emptyStringQueryParams.lastIndex) { + result.add(and) + } + } } private fun getBooleanQueryParams(queryParams: List): List { return queryParams.filter { it.getGeneForQuery() is BooleanGene && (it.getGeneForQuery() as BooleanGene).value } } + private fun getNumberQueryParams(queryParams: List): List { + return queryParams.filter { it.getGeneForQuery() is NumberGene<*> && (it.getGeneForQuery() as NumberGene<*>).value.toLong() < 0 } + } + + private fun getEmptyStringQueryParams(queryParams: List): List { + return queryParams.filter { it.getGeneForQuery() is StringGene && (it.getGeneForQuery() as StringGene).getValueAsRawString().trim().isEmpty() } + } + private fun removeSolvedDuplicates(duplicatedIndividuals: MutableSet>, disambiguatedIndividuals: Set>) { duplicatedIndividuals.removeAll(disambiguatedIndividuals) } From b66993db375eca7f4f434d55212dc59127040cdb Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Fri, 29 Nov 2024 02:44:49 -0300 Subject: [PATCH 02/15] plan for next disambiguation --- .../RestActionTestCaseNamingStrategy.kt | 46 +++++++++++++++---- .../core/output/service/TestCaseWriter.kt | 20 ++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index cb8f48c074..cc33e20af8 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -2,6 +2,7 @@ package org.evomaster.core.output.naming import com.google.gson.Gson import com.google.gson.JsonSyntaxException +import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult @@ -14,6 +15,8 @@ import org.evomaster.core.search.action.EvaluatedAction import org.evomaster.core.search.gene.BooleanGene import org.evomaster.core.search.gene.numeric.NumberGene import org.evomaster.core.search.gene.string.StringGene +import org.slf4j.Logger +import org.slf4j.LoggerFactory import javax.ws.rs.core.MediaType open class RestActionTestCaseNamingStrategy( @@ -22,17 +25,26 @@ open class RestActionTestCaseNamingStrategy( private val nameWithQueryParameters: Boolean, ) : ActionTestCaseNamingStrategy(solution, languageConventionFormatter) { + companion object { + private val log: Logger = LoggerFactory.getLogger(RestActionTestCaseNamingStrategy::class.java) + } + + // this will get a list of ambiguitySolver functions and apply them all override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as RestCallAction nameTokens.add(action.verb.toString().lowercase()) nameTokens.add(on) + // val resultTokens = getResult(individual) if (ambiguitySolver == null) { nameTokens.add(getPath(action.path.nameQualifier)) } else { + // if len(name) + len(resultTokens) + len(solverResult) <= MAX_CHARS ---> OK + // else keep only name + acceptedSolverResults + resultTokens nameTokens.addAll(ambiguitySolver(action)) } + // will change addResult for getResult, that will allow for disambiguation to check if it's exceeding max chars and thus apply the disamb function or not addResult(individual, nameTokens) return formatName(nameTokens) @@ -60,10 +72,12 @@ open class RestActionTestCaseNamingStrategy( val workingCopy = duplicatedIndividuals.toMutableSet() val pathDisambiguatedIndividuals = solvePathAmbiguities(workingCopy) + log.warn("Solved ${pathDisambiguatedIndividuals.size} path ambiguities: $pathDisambiguatedIndividuals") solvedAmbiguities.putAll(pathDisambiguatedIndividuals) removeSolvedDuplicates(workingCopy, pathDisambiguatedIndividuals.keys) val queryParamsDisambiguatedIndividuals = solveQueryParamsAmbiguities(workingCopy) + log.warn("Solved ${queryParamsDisambiguatedIndividuals.size} query param ambiguities: $queryParamsDisambiguatedIndividuals") solvedAmbiguities.putAll(queryParamsDisambiguatedIndividuals) removeSolvedDuplicates(workingCopy, queryParamsDisambiguatedIndividuals.keys) @@ -87,10 +101,11 @@ open class RestActionTestCaseNamingStrategy( val isLastAParam = path.isLastElementAParameter() Pair(toStringPath, isLastAParam) } - .filter { it.value.size == 1 && !it.key.second } - .mapNotNull { entry -> - val eInd = entry.value[0] - eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) + .filter { it.value.isNotEmpty() && !it.key.second } + .flatMap { entry -> + entry.value.map { it to expandName(it, mutableListOf(), ::pathAmbiguitySolver) } +// val eInd = entry.value[0] +// eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) } .toMap() } @@ -128,12 +143,21 @@ open class RestActionTestCaseNamingStrategy( private fun solveQueryParamsAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { return duplicatedIndividuals .groupBy { - (it.evaluatedMainActions().last().action as RestCallAction).parameters.filterIsInstance() + val restAction = it.evaluatedMainActions().last().action as RestCallAction + val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) +// (it.evaluatedMainActions().last().action as RestCallAction).parameters.filterIsInstance() + restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } } - .filter { it.value.size == 1 && it.key.isNotEmpty()} - .mapNotNull { entry -> - val eInd = entry.value[0] - eInd to expandName(eInd, mutableListOf(), ::queryParamsAmbiguitySolver) +// .filter { it.value.size == 1 && it.key.isNotEmpty()} + .filter { it.value.isNotEmpty() && it.key.isNotEmpty()} +// .mapNotNull { entry -> +// val eInd = entry.value[0] +// eInd to expandName(eInd, mutableListOf(), ::queryParamsAmbiguitySolver) +// } + .flatMap { entry -> + entry.value.map { it to expandName(it, mutableListOf(), ::queryParamsAmbiguitySolver) } +// val eInd = entry.value[0] +// eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) } .toMap() } @@ -146,7 +170,9 @@ open class RestActionTestCaseNamingStrategy( val result = mutableListOf() result.add(getPath(restAction.path.nameQualifier)) - val queryParams = restAction.parameters.filterIsInstance() + val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) +// val queryParams = restAction.parameters.filterIsInstance() + val queryParams = restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } result.add(with) result.add(if (queryParams.size > 1) "${queryParam}s" else queryParam) if (nameWithQueryParameters) { diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt index 114dc29eaa..614a59811d 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt @@ -7,6 +7,7 @@ import org.evomaster.core.output.OutputFormat import org.evomaster.core.output.TestCase import org.evomaster.core.output.TestWriterUtils import org.evomaster.core.output.TestWriterUtils.getWireMockVariableName +import org.evomaster.core.problem.api.ApiWsAction import org.evomaster.core.problem.enterprise.EnterpriseActionResult import org.evomaster.core.problem.externalservice.HostnameResolutionAction import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction @@ -75,6 +76,25 @@ abstract class TestCaseWriter { clusterComment(lines, test) } + lines.addSingleCommentLine("Individual:") + lines.addSingleCommentLine("\tActions:") + test.test.individual.seeAllActions().forEach { ac -> + lines.addSingleCommentLine("\t\t${ac.javaClass.kotlin.qualifiedName}: ${ac.getName()}") + lines.addSingleCommentLine("\t\t\tAction parameters:") + (ac as ApiWsAction).parameters.forEach { acParam -> + lines.addSingleCommentLine("\t\t\t\t${acParam.name}: '${acParam.primaryGene().getValueAsRawString()}'") + } + lines.addSingleCommentLine("\t\t\tGenes:") + ac.seeTopGenes().forEach { gene -> + lines.addSingleCommentLine("\t\t\t\t${gene.javaClass.kotlin.qualifiedName} = ${gene.getVariableName()}:${gene.getValueAsRawString()}") + } + } + lines.addSingleCommentLine("\tEvaluated Actions:") + test.test.evaluatedMainActions().forEach { eAc -> + lines.addSingleCommentLine("\t\t${eAc.action.javaClass.kotlin.qualifiedName}: ${eAc.action.getName()}") + } + + if (format.isJUnit()) { if (config.testTimeout <= 0) { lines.add("@Test") From 248d9aa3c23c5dd22c3231b2746295d8ed730d24 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Wed, 4 Dec 2024 23:43:57 -0300 Subject: [PATCH 03/15] Chained solvers for rest disambiguation --- .../GraphQLActionTestCaseNamingStrategy.kt | 2 +- ...ingHelperNumberedTestCaseNamingStrategy.kt | 2 +- .../naming/NumberedTestCaseNamingStrategy.kt | 2 +- .../naming/RPCActionTestCaseNamingStrategy.kt | 2 +- .../RestActionTestCaseNamingStrategy.kt | 70 +++++++++++++------ .../output/naming/TestCaseNamingStrategy.kt | 2 +- .../evomaster/core/problem/rest/RestPath.kt | 13 +++- .../naming/TestCaseDisambiguationTest.kt | 22 +++--- 8 files changed, 76 insertions(+), 39 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/GraphQLActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/GraphQLActionTestCaseNamingStrategy.kt index 2d9e5ece2e..b135d558dd 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/GraphQLActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/GraphQLActionTestCaseNamingStrategy.kt @@ -13,7 +13,7 @@ open class GraphQLActionTestCaseNamingStrategy( ) : ActionTestCaseNamingStrategy(solution, languageConventionFormatter) { - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { + override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as GraphQLAction diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/NamingHelperNumberedTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/NamingHelperNumberedTestCaseNamingStrategy.kt index e2c8262d01..1328fd908b 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/NamingHelperNumberedTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/NamingHelperNumberedTestCaseNamingStrategy.kt @@ -11,7 +11,7 @@ class NamingHelperNumberedTestCaseNamingStrategy( private val namingHelper = NamingHelper() - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { + override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { return namingHelper.suggestName(individual) } } diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/NumberedTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/NumberedTestCaseNamingStrategy.kt index 89d847c316..39d5eb86ab 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/NumberedTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/NumberedTestCaseNamingStrategy.kt @@ -22,7 +22,7 @@ open class NumberedTestCaseNamingStrategy( } // numbered strategy will not expand the name unless it is using the namingHelper - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { + override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { return "" } diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RPCActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RPCActionTestCaseNamingStrategy.kt index d25de90986..db3718e367 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RPCActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RPCActionTestCaseNamingStrategy.kt @@ -14,7 +14,7 @@ open class RPCActionTestCaseNamingStrategy( languageConventionFormatter: LanguageConventionFormatter ) : ActionTestCaseNamingStrategy(solution, languageConventionFormatter) { - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { + override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as RPCCallAction diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index cc33e20af8..c14681320d 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -30,19 +30,20 @@ open class RestActionTestCaseNamingStrategy( } // this will get a list of ambiguitySolver functions and apply them all - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)?): String { + override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as RestCallAction nameTokens.add(action.verb.toString().lowercase()) nameTokens.add(on) // val resultTokens = getResult(individual) - if (ambiguitySolver == null) { + if (ambiguitySolvers.isEmpty()) { nameTokens.add(getPath(action.path.nameQualifier)) } else { // if len(name) + len(resultTokens) + len(solverResult) <= MAX_CHARS ---> OK // else keep only name + acceptedSolverResults + resultTokens - nameTokens.addAll(ambiguitySolver(action)) + ambiguitySolvers.forEach { solver -> nameTokens.addAll(solver(action)) } +// nameTokens.addAll(ambiguitySolver(action)) } // will change addResult for getResult, that will allow for disambiguation to check if it's exceeding max chars and thus apply the disamb function or not addResult(individual, nameTokens) @@ -71,17 +72,35 @@ open class RestActionTestCaseNamingStrategy( val solvedAmbiguities = mutableMapOf, String>() val workingCopy = duplicatedIndividuals.toMutableSet() - val pathDisambiguatedIndividuals = solvePathAmbiguities(workingCopy) + val ambiguitySolversPerIndividual = mutableMapOf, MutableList<(Action) -> List>>() + + val pathDisambiguatedIndividuals = getPathDisambiguationIndividuals(workingCopy) + pathDisambiguatedIndividuals.forEach { + ambiguitySolversPerIndividual[it] = mutableListOf(::pathAmbiguitySolver) + } log.warn("Solved ${pathDisambiguatedIndividuals.size} path ambiguities: $pathDisambiguatedIndividuals") - solvedAmbiguities.putAll(pathDisambiguatedIndividuals) - removeSolvedDuplicates(workingCopy, pathDisambiguatedIndividuals.keys) +// solvedAmbiguities.putAll(pathDisambiguatedIndividuals) +// removeSolvedDuplicates(workingCopy, pathDisambiguatedIndividuals.keys) - val queryParamsDisambiguatedIndividuals = solveQueryParamsAmbiguities(workingCopy) + val queryParamsDisambiguatedIndividuals = getQueryParamsDisambiguationIndividuals(workingCopy) log.warn("Solved ${queryParamsDisambiguatedIndividuals.size} query param ambiguities: $queryParamsDisambiguatedIndividuals") - solvedAmbiguities.putAll(queryParamsDisambiguatedIndividuals) - removeSolvedDuplicates(workingCopy, queryParamsDisambiguatedIndividuals.keys) + queryParamsDisambiguatedIndividuals.forEach { + if (ambiguitySolversPerIndividual.containsKey(it)) { + ambiguitySolversPerIndividual[it]?.add(::queryParamsAmbiguitySolver) + } else { + ambiguitySolversPerIndividual[it] = mutableListOf(::queryParamsAmbiguitySolver) + } + } +// solvedAmbiguities.putAll(queryParamsDisambiguatedIndividuals) +// removeSolvedDuplicates(workingCopy, queryParamsDisambiguatedIndividuals.keys) + + val res = ambiguitySolversPerIndividual.map { + it.key to expandName(it.key, mutableListOf(), it.value) + }.toMap() - return solvedAmbiguities + return res + +// return solvedAmbiguities } /* @@ -90,7 +109,8 @@ open class RestActionTestCaseNamingStrategy( * differs and when said individual does not have a parameter as a last element since it might differ in the * parameter name but not the rest of the path. */ - private fun solvePathAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { +// private fun solvePathAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { + private fun getPathDisambiguationIndividuals(duplicatedIndividuals: MutableSet>): List> { return duplicatedIndividuals .groupBy { var path = (it.evaluatedMainActions().last().action as RestCallAction).path @@ -103,13 +123,17 @@ open class RestActionTestCaseNamingStrategy( } .filter { it.value.isNotEmpty() && !it.key.second } .flatMap { entry -> - entry.value.map { it to expandName(it, mutableListOf(), ::pathAmbiguitySolver) } -// val eInd = entry.value[0] +// entry.value.map { it to expandName(it, mutableListOf(), ::pathAmbiguitySolver) } + val eInd = entry.value[0] + entry.value // eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) } - .toMap() + .toList() +// .toMap() + } + /* * If the last element of a path is a parameter then we must go up a level * @@ -140,13 +164,16 @@ open class RestActionTestCaseNamingStrategy( * The filter call ensures that we are only performing this disambiguation when there's only one individual that * differs and the list of query params is not empty. */ - private fun solveQueryParamsAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { +// private fun solveQueryParamsAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { + private fun getQueryParamsDisambiguationIndividuals(duplicatedIndividuals: MutableSet>): List> { return duplicatedIndividuals .groupBy { val restAction = it.evaluatedMainActions().last().action as RestCallAction val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) // (it.evaluatedMainActions().last().action as RestCallAction).parameters.filterIsInstance() - restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } +// val b = restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } +// b + restAction.path.getOnlyQuery(restAction.parameters) } // .filter { it.value.size == 1 && it.key.isNotEmpty()} .filter { it.value.isNotEmpty() && it.key.isNotEmpty()} @@ -155,11 +182,12 @@ open class RestActionTestCaseNamingStrategy( // eInd to expandName(eInd, mutableListOf(), ::queryParamsAmbiguitySolver) // } .flatMap { entry -> - entry.value.map { it to expandName(it, mutableListOf(), ::queryParamsAmbiguitySolver) } +// entry.value.map { it to expandName(it, mutableListOf(), ::queryParamsAmbiguitySolver) } // val eInd = entry.value[0] // eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) - } - .toMap() + entry.value + }.toList() +// .toMap() } /* @@ -168,11 +196,11 @@ open class RestActionTestCaseNamingStrategy( private fun queryParamsAmbiguitySolver(action: Action): List { val restAction = action as RestCallAction val result = mutableListOf() - result.add(getPath(restAction.path.nameQualifier)) +// result.add(getPath(restAction.path.nameQualifier)) val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) // val queryParams = restAction.parameters.filterIsInstance() - val queryParams = restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } + val queryParams = restAction.path.getOnlyQuery(restAction.parameters) result.add(with) result.add(if (queryParams.size > 1) "${queryParam}s" else queryParam) if (nameWithQueryParameters) { diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt index 165674dfeb..b6bba292d3 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt @@ -32,7 +32,7 @@ abstract class TestCaseNamingStrategy( * * @return a String with extra information that will be included in the test name, regarding the EvaluatedIndividual */ - protected abstract fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolver: ((Action) -> List)? = null): String + protected abstract fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List> = emptyList()): String /** * @param duplicatedIndividuals set containing the EvaluatedIndividuals sharing the same name diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt index bd74d67aca..a674e528d6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt @@ -297,11 +297,18 @@ class RestPath(path: String) { return params.filter(usableQueryParamsFunction()).size } - fun resolveOnlyQuery(params: List): List { - + fun getOnlyQuery(params: List): List { return params .filter(usableQueryParamsFunction()) .filterIsInstance() + } + + fun resolveOnlyQuery(params: List): List { + +// return params +// .filter(usableQueryParamsFunction()) +// .filterIsInstance() + return getOnlyQuery(params) .map { q -> val name = encode(q.name) @@ -581,4 +588,4 @@ class RestPath(path: String) { val path = doComputeToString(reduced, false) return RestPath(path) } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index 0e1b89f36e..622b60ddfb 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -74,8 +74,8 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(3, testCases.size) assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesReturnsEmpty", testCases[1].name) - assertEquals("test_2_getOnLanguagesReturnsEmpty", testCases[2].name) + assertEquals("test_1_getOnSyntaxLanguagesReturnsEmpty", testCases[1].name) + assertEquals("test_2_getOnSyntaxLanguagesReturnsEmpty", testCases[2].name) } @Test @@ -110,7 +110,7 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamReturnsEmpty", testCases[1].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamReturnsEmpty", testCases[1].name) } @Test @@ -125,7 +125,7 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamsReturnsEmpty", testCases[1].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsReturnsEmpty", testCases[1].name) } @Test @@ -142,7 +142,7 @@ class TestCaseDisambiguationTest { assertEquals(3, testCases.size) assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_1_getOnSyntaxLanguagesReturnsEmpty", testCases[1].name) - assertEquals("test_2_getOnLanguagesWithQueryParamReturnsEmpty", testCases[2].name) + assertEquals("test_2_getOnSyntaxLanguagesWithQueryParamReturnsEmpty", testCases[2].name) } @Test @@ -156,8 +156,8 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) - assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesReturnsEmpty", testCases[1].name) + assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnSyntaxLanguagesReturnsEmpty", testCases[1].name) } @Test @@ -173,7 +173,7 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamMyQueryParamReturnsEmpty", testCases[1].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamMyQueryParamReturnsEmpty", testCases[1].name) } @Test @@ -188,8 +188,9 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) +// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamReturnsEmpty", testCases[1].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamReturnsEmpty", testCases[1].name) } @Test @@ -206,8 +207,9 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) +// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) } private fun getPathParam(paramName: String): Param { From 10b8d598f5c88616fcc417cfe36a30483c2295d2 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Sun, 8 Dec 2024 21:37:29 -0300 Subject: [PATCH 04/15] Query param values inspected for experimental action test case naming --- .../RestActionTestCaseNamingStrategy.kt | 87 ++++++++----------- .../core/output/service/TestCaseWriter.kt | 20 ----- .../naming/TestCaseDisambiguationTest.kt | 54 +++++++++--- 3 files changed, 75 insertions(+), 86 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index c14681320d..08aa70528d 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -2,7 +2,6 @@ package org.evomaster.core.output.naming import com.google.gson.Gson import com.google.gson.JsonSyntaxException -import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult @@ -13,7 +12,9 @@ import org.evomaster.core.search.Solution import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.EvaluatedAction import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.NumberGene +import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.string.StringGene import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -36,14 +37,12 @@ open class RestActionTestCaseNamingStrategy( nameTokens.add(action.verb.toString().lowercase()) nameTokens.add(on) - // val resultTokens = getResult(individual) if (ambiguitySolvers.isEmpty()) { nameTokens.add(getPath(action.path.nameQualifier)) } else { - // if len(name) + len(resultTokens) + len(solverResult) <= MAX_CHARS ---> OK + // TODO: max chars check. Idea: if len(name) + len(resultTokens) + len(foreach:solverResult) <= MAX_CHARS ---> OK // else keep only name + acceptedSolverResults + resultTokens ambiguitySolvers.forEach { solver -> nameTokens.addAll(solver(action)) } -// nameTokens.addAll(ambiguitySolver(action)) } // will change addResult for getResult, that will allow for disambiguation to check if it's exceeding max chars and thus apply the disamb function or not addResult(individual, nameTokens) @@ -69,7 +68,6 @@ open class RestActionTestCaseNamingStrategy( * following solvers. */ override fun resolveAmbiguities(duplicatedIndividuals: Set>): Map, String> { - val solvedAmbiguities = mutableMapOf, String>() val workingCopy = duplicatedIndividuals.toMutableSet() val ambiguitySolversPerIndividual = mutableMapOf, MutableList<(Action) -> List>>() @@ -78,12 +76,8 @@ open class RestActionTestCaseNamingStrategy( pathDisambiguatedIndividuals.forEach { ambiguitySolversPerIndividual[it] = mutableListOf(::pathAmbiguitySolver) } - log.warn("Solved ${pathDisambiguatedIndividuals.size} path ambiguities: $pathDisambiguatedIndividuals") -// solvedAmbiguities.putAll(pathDisambiguatedIndividuals) -// removeSolvedDuplicates(workingCopy, pathDisambiguatedIndividuals.keys) val queryParamsDisambiguatedIndividuals = getQueryParamsDisambiguationIndividuals(workingCopy) - log.warn("Solved ${queryParamsDisambiguatedIndividuals.size} query param ambiguities: $queryParamsDisambiguatedIndividuals") queryParamsDisambiguatedIndividuals.forEach { if (ambiguitySolversPerIndividual.containsKey(it)) { ambiguitySolversPerIndividual[it]?.add(::queryParamsAmbiguitySolver) @@ -91,16 +85,21 @@ open class RestActionTestCaseNamingStrategy( ambiguitySolversPerIndividual[it] = mutableListOf(::queryParamsAmbiguitySolver) } } -// solvedAmbiguities.putAll(queryParamsDisambiguatedIndividuals) -// removeSolvedDuplicates(workingCopy, queryParamsDisambiguatedIndividuals.keys) - val res = ambiguitySolversPerIndividual.map { - it.key to expandName(it.key, mutableListOf(), it.value) - }.toMap() - - return res + return collectSolvedNames(ambiguitySolversPerIndividual) + } -// return solvedAmbiguities + /* + * When two or more individuals share a name, no disambiguation is performed. + * Otherwise, we would just be increasing test case name length without having actually disambiguated. + */ + private fun collectSolvedNames(ambiguitySolversPerIndividual: MutableMap, MutableList<(Action) -> List>>): Map, String> { + return ambiguitySolversPerIndividual + .map { it.key to expandName(it.key, mutableListOf(), it.value) } + .groupBy({ it.second }, { it.first }) + .filter { it.value.size == 1 } + .flatMap { entry -> entry.value.map { key -> key to entry.key } } + .toMap() } /* @@ -109,7 +108,6 @@ open class RestActionTestCaseNamingStrategy( * differs and when said individual does not have a parameter as a last element since it might differ in the * parameter name but not the rest of the path. */ -// private fun solvePathAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { private fun getPathDisambiguationIndividuals(duplicatedIndividuals: MutableSet>): List> { return duplicatedIndividuals .groupBy { @@ -122,18 +120,11 @@ open class RestActionTestCaseNamingStrategy( Pair(toStringPath, isLastAParam) } .filter { it.value.isNotEmpty() && !it.key.second } - .flatMap { entry -> -// entry.value.map { it to expandName(it, mutableListOf(), ::pathAmbiguitySolver) } - val eInd = entry.value[0] - entry.value -// eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) - } + .flatMap { it.value } .toList() -// .toMap() } - /* * If the last element of a path is a parameter then we must go up a level * @@ -164,30 +155,14 @@ open class RestActionTestCaseNamingStrategy( * The filter call ensures that we are only performing this disambiguation when there's only one individual that * differs and the list of query params is not empty. */ -// private fun solveQueryParamsAmbiguities(duplicatedIndividuals: MutableSet>): Map, String> { private fun getQueryParamsDisambiguationIndividuals(duplicatedIndividuals: MutableSet>): List> { return duplicatedIndividuals .groupBy { val restAction = it.evaluatedMainActions().last().action as RestCallAction - val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) -// (it.evaluatedMainActions().last().action as RestCallAction).parameters.filterIsInstance() -// val b = restAction.parameters.filterIsInstance().filter { param: Param -> resolvedQueryParams.contains(param.name) } -// b restAction.path.getOnlyQuery(restAction.parameters) } -// .filter { it.value.size == 1 && it.key.isNotEmpty()} .filter { it.value.isNotEmpty() && it.key.isNotEmpty()} -// .mapNotNull { entry -> -// val eInd = entry.value[0] -// eInd to expandName(eInd, mutableListOf(), ::queryParamsAmbiguitySolver) -// } - .flatMap { entry -> -// entry.value.map { it to expandName(it, mutableListOf(), ::queryParamsAmbiguitySolver) } -// val eInd = entry.value[0] -// eInd to expandName(eInd, mutableListOf(), ::pathAmbiguitySolver) - entry.value - }.toList() -// .toMap() + .flatMap { it.value }.toList() } /* @@ -196,10 +171,7 @@ open class RestActionTestCaseNamingStrategy( private fun queryParamsAmbiguitySolver(action: Action): List { val restAction = action as RestCallAction val result = mutableListOf() -// result.add(getPath(restAction.path.nameQualifier)) - val resolvedQueryParams = restAction.path.resolveOnlyQuery(restAction.parameters) -// val queryParams = restAction.parameters.filterIsInstance() val queryParams = restAction.path.getOnlyQuery(restAction.parameters) result.add(with) result.add(if (queryParams.size > 1) "${queryParam}s" else queryParam) @@ -218,7 +190,7 @@ open class RestActionTestCaseNamingStrategy( } } - val numberQueryParams = getNumberQueryParams(queryParams) + val numberQueryParams = getNegativeNumberQueryParams(queryParams) numberQueryParams.forEachIndexed { index, queryParam -> result.add("negative") result.add(queryParam.name) @@ -238,19 +210,28 @@ open class RestActionTestCaseNamingStrategy( } private fun getBooleanQueryParams(queryParams: List): List { - return queryParams.filter { it.getGeneForQuery() is BooleanGene && (it.getGeneForQuery() as BooleanGene).value } + return queryParams.filter { + val wrappedGene = getWrappedGene(it) + wrappedGene is BooleanGene && wrappedGene.value + } } - private fun getNumberQueryParams(queryParams: List): List { - return queryParams.filter { it.getGeneForQuery() is NumberGene<*> && (it.getGeneForQuery() as NumberGene<*>).value.toLong() < 0 } + private fun getNegativeNumberQueryParams(queryParams: List): List { + return queryParams.filter { + val wrappedGene = getWrappedGene(it) + wrappedGene is NumberGene<*> && wrappedGene.value.toLong() < 0 + } } private fun getEmptyStringQueryParams(queryParams: List): List { - return queryParams.filter { it.getGeneForQuery() is StringGene && (it.getGeneForQuery() as StringGene).getValueAsRawString().trim().isEmpty() } + return queryParams.filter { + val wrappedGene = getWrappedGene(it) + wrappedGene is StringGene && wrappedGene.getValueAsRawString().trim().isEmpty() + } } - private fun removeSolvedDuplicates(duplicatedIndividuals: MutableSet>, disambiguatedIndividuals: Set>) { - duplicatedIndividuals.removeAll(disambiguatedIndividuals) + private fun getWrappedGene(queryParam: QueryParam): Gene { + return (queryParam.getGeneForQuery() as OptionalGene).gene } private fun isGetCall(evaluatedAction: EvaluatedAction): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt index 614a59811d..114dc29eaa 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt @@ -7,7 +7,6 @@ import org.evomaster.core.output.OutputFormat import org.evomaster.core.output.TestCase import org.evomaster.core.output.TestWriterUtils import org.evomaster.core.output.TestWriterUtils.getWireMockVariableName -import org.evomaster.core.problem.api.ApiWsAction import org.evomaster.core.problem.enterprise.EnterpriseActionResult import org.evomaster.core.problem.externalservice.HostnameResolutionAction import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction @@ -76,25 +75,6 @@ abstract class TestCaseWriter { clusterComment(lines, test) } - lines.addSingleCommentLine("Individual:") - lines.addSingleCommentLine("\tActions:") - test.test.individual.seeAllActions().forEach { ac -> - lines.addSingleCommentLine("\t\t${ac.javaClass.kotlin.qualifiedName}: ${ac.getName()}") - lines.addSingleCommentLine("\t\t\tAction parameters:") - (ac as ApiWsAction).parameters.forEach { acParam -> - lines.addSingleCommentLine("\t\t\t\t${acParam.name}: '${acParam.primaryGene().getValueAsRawString()}'") - } - lines.addSingleCommentLine("\t\t\tGenes:") - ac.seeTopGenes().forEach { gene -> - lines.addSingleCommentLine("\t\t\t\t${gene.javaClass.kotlin.qualifiedName} = ${gene.getVariableName()}:${gene.getValueAsRawString()}") - } - } - lines.addSingleCommentLine("\tEvaluated Actions:") - test.test.evaluatedMainActions().forEach { eAc -> - lines.addSingleCommentLine("\t\t${eAc.action.javaClass.kotlin.qualifiedName}: ${eAc.action.getName()}") - } - - if (format.isJUnit()) { if (config.testTimeout <= 0) { lines.add("@Test") diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index 622b60ddfb..9de54e717c 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -14,6 +14,7 @@ import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Solution import org.evomaster.core.search.gene.BooleanGene import org.evomaster.core.search.gene.optional.CustomMutationRateGene +import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.string.StringGene import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -74,8 +75,8 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(3, testCases.size) assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnSyntaxLanguagesReturnsEmpty", testCases[1].name) - assertEquals("test_2_getOnSyntaxLanguagesReturnsEmpty", testCases[2].name) + assertEquals("test_1_getOnLanguagesReturnsEmpty", testCases[1].name) + assertEquals("test_2_getOnLanguagesReturnsEmpty", testCases[2].name) } @Test @@ -102,6 +103,7 @@ class TestCaseDisambiguationTest { fun pathWithQueryParamDisambiguation() { val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividualWithQP = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = singletonList(getStringQueryParam("myQueryParam")))) + ensureGeneValue(syntaxLanguagesIndividualWithQP, "myQueryParam", "aStringValue") val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividualWithQP), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -117,6 +119,8 @@ class TestCaseDisambiguationTest { fun pathWithMoreThanOneQueryParamDisambiguation() { val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividualWithQP = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = mutableListOf(getStringQueryParam("myQueryParam"), getStringQueryParam("myOtherQueryParam")))) + ensureGeneValue(syntaxLanguagesIndividualWithQP, "myQueryParam", "aStringValue") + ensureGeneValue(syntaxLanguagesIndividualWithQP, "myOtherQueryParam", "anotherStringValue") val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividualWithQP), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -133,6 +137,7 @@ class TestCaseDisambiguationTest { val languagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividualWithQP = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = singletonList(getStringQueryParam("myQueryParam")))) + ensureGeneValue(syntaxLanguagesIndividualWithQP, "myQueryParam", "aStringValue") val solution = Solution(mutableListOf(languagesIndividual, syntaxLanguagesIndividual, syntaxLanguagesIndividualWithQP), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -156,15 +161,15 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) - assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnSyntaxLanguagesReturnsEmpty", testCases[1].name) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesReturnsEmpty", testCases[1].name) } @Test fun oneTrueBooleanQueryParamIsAdded() { val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividual2 = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = singletonList(getBooleanQueryParam("myQueryParam")))) - ensureBooleanGeneValue(syntaxLanguagesIndividual2, "myQueryParam", "true") + ensureGeneValue(syntaxLanguagesIndividual2, "myQueryParam", "true") val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividual2), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -180,7 +185,7 @@ class TestCaseDisambiguationTest { fun oneFalseBooleanQueryParamIsNotAdded() { val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividual2 = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = singletonList(getBooleanQueryParam("myQueryParam")))) - ensureBooleanGeneValue(syntaxLanguagesIndividual2, "myQueryParam", "false") + ensureGeneValue(syntaxLanguagesIndividual2, "myQueryParam", "false") val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividual2), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -197,9 +202,28 @@ class TestCaseDisambiguationTest { fun onlyTrueBooleanQueryParamsAreAdded() { val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) val syntaxLanguagesIndividual2 = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = mutableListOf(getBooleanQueryParam("firstParam"), getBooleanQueryParam("secondParam"), getStringQueryParam("thirdParam"), getBooleanQueryParam("fourthParam")))) - ensureBooleanGeneValue(syntaxLanguagesIndividual2, "firstParam", "true") - ensureBooleanGeneValue(syntaxLanguagesIndividual2, "secondParam", "false") - ensureBooleanGeneValue(syntaxLanguagesIndividual2, "fourthParam", "true") + ensureGeneValue(syntaxLanguagesIndividual2, "firstParam", "true") + ensureGeneValue(syntaxLanguagesIndividual2, "secondParam", "false") + ensureGeneValue(syntaxLanguagesIndividual2, "fourthParam", "true") + + val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividual2), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) +// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) + } + + @Test + fun negativeNumberQueryParamsAreAdded() { + val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) + val syntaxLanguagesIndividual2 = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = mutableListOf(getBooleanQueryParam("firstParam"), getBooleanQueryParam("secondParam"), getStringQueryParam("thirdParam"), getBooleanQueryParam("fourthParam")))) + ensureGeneValue(syntaxLanguagesIndividual2, "firstParam", "true") + ensureGeneValue(syntaxLanguagesIndividual2, "secondParam", "false") + ensureGeneValue(syntaxLanguagesIndividual2, "fourthParam", "true") val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividual2), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) @@ -217,20 +241,24 @@ class TestCaseDisambiguationTest { } private fun getStringQueryParam(paramName: String): Param { - return QueryParam(paramName, StringGene(paramName)) + return QueryParam(paramName, OptionalGene(paramName, StringGene(paramName))) } private fun getBooleanQueryParam(paramName: String): Param { - return QueryParam(paramName, BooleanGene(paramName)) + return QueryParam(paramName, OptionalGene(paramName, BooleanGene(paramName))) } /* Since the randomization used to construct the evaluated individuals might set a random boolean value, we do this to ensure the one we want for unit testing */ - private fun ensureBooleanGeneValue(evaluatedIndividual: EvaluatedIndividual, paramName: String, paramValue: String) { + private fun ensureGeneValue(evaluatedIndividual: EvaluatedIndividual, paramName: String, paramValue: String) { val restCallAction = evaluatedIndividual.evaluatedMainActions().last().action as RestCallAction - (restCallAction.parameters.filter { it.name == paramName }).forEach { (it as QueryParam).getGeneForQuery().setFromStringValue(paramValue) } + (restCallAction.parameters.filter { it.name == paramName }).forEach { +// val optionalGene = (it as QueryParam).getGeneForQuery() as OptionalGene + + (it as QueryParam).getGeneForQuery().setFromStringValue(paramValue) + } } } From 6812b0d32c10af202d30bb1505e40e516c278da6 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Sun, 8 Dec 2024 21:45:16 -0300 Subject: [PATCH 05/15] removed comments and extra lines --- .../core/output/naming/RestActionTestCaseNamingStrategy.kt | 1 - .../main/kotlin/org/evomaster/core/problem/rest/RestPath.kt | 4 ---- .../core/output/naming/TestCaseDisambiguationTest.kt | 5 ----- 3 files changed, 10 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 08aa70528d..56a2afd862 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -44,7 +44,6 @@ open class RestActionTestCaseNamingStrategy( // else keep only name + acceptedSolverResults + resultTokens ambiguitySolvers.forEach { solver -> nameTokens.addAll(solver(action)) } } - // will change addResult for getResult, that will allow for disambiguation to check if it's exceeding max chars and thus apply the disamb function or not addResult(individual, nameTokens) return formatName(nameTokens) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt index a674e528d6..f06c7882e5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt @@ -304,10 +304,6 @@ class RestPath(path: String) { } fun resolveOnlyQuery(params: List): List { - -// return params -// .filter(usableQueryParamsFunction()) -// .filterIsInstance() return getOnlyQuery(params) .map { q -> val name = encode(q.name) diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index 9de54e717c..d519b249bd 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -193,7 +193,6 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) -// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamReturnsEmpty", testCases[1].name) } @@ -212,7 +211,6 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) -// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) } @@ -231,7 +229,6 @@ class TestCaseDisambiguationTest { val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) -// assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) } @@ -255,8 +252,6 @@ class TestCaseDisambiguationTest { private fun ensureGeneValue(evaluatedIndividual: EvaluatedIndividual, paramName: String, paramValue: String) { val restCallAction = evaluatedIndividual.evaluatedMainActions().last().action as RestCallAction (restCallAction.parameters.filter { it.name == paramName }).forEach { -// val optionalGene = (it as QueryParam).getGeneForQuery() as OptionalGene - (it as QueryParam).getGeneForQuery().setFromStringValue(paramValue) } } From 8e6feb1349c522fd4d827fdb2885b2fb2d1dfd3d Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Sun, 8 Dec 2024 21:56:20 -0300 Subject: [PATCH 06/15] Negative number and empty string query parameter tests in test naming --- .../RestActionTestCaseNamingStrategy.kt | 1 - .../naming/TestCaseDisambiguationTest.kt | 54 +++++++++++++++---- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 56a2afd862..1e7fd9230a 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -30,7 +30,6 @@ open class RestActionTestCaseNamingStrategy( private val log: Logger = LoggerFactory.getLogger(RestActionTestCaseNamingStrategy::class.java) } - // this will get a list of ambiguitySolver functions and apply them all override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as RestCallAction diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index d519b249bd..1dacb1a021 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -13,6 +13,7 @@ import org.evomaster.core.problem.rest.param.QueryParam import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Solution import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.string.StringGene @@ -216,21 +217,52 @@ class TestCaseDisambiguationTest { } @Test - fun negativeNumberQueryParamsAreAdded() { - val syntaxLanguagesIndividual = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages")) - val syntaxLanguagesIndividual2 = getEvaluatedIndividualWith(getRestCallAction("/syntax/languages", parameters = mutableListOf(getBooleanQueryParam("firstParam"), getBooleanQueryParam("secondParam"), getStringQueryParam("thirdParam"), getBooleanQueryParam("fourthParam")))) - ensureGeneValue(syntaxLanguagesIndividual2, "firstParam", "true") - ensureGeneValue(syntaxLanguagesIndividual2, "secondParam", "false") - ensureGeneValue(syntaxLanguagesIndividual2, "fourthParam", "true") + fun negativeNumberQueryParamIsAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val negativeQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getIntegerQueryParam("limit")))) + ensureGeneValue(negativeQPIndividual, "limit", "-1") - val solution = Solution(mutableListOf(syntaxLanguagesIndividual, syntaxLanguagesIndividual2), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + val solution = Solution(mutableListOf(simpleIndividual, negativeQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) val testCases = namingStrategy.getTestCases() assertEquals(2, testCases.size) - assertEquals("test_0_getOnSyntaxLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnSyntaxLanguagesWithQueryParamsFirstParamAndFourthParamReturnsEmpty", testCases[1].name) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamNegativeLimitReturnsEmpty", testCases[1].name) + } + + @Test + fun emptyStringQueryParamIsAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val emptyStringIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getStringQueryParam("name")))) + ensureGeneValue(emptyStringIndividual, "name", "") + + val solution = Solution(mutableListOf(simpleIndividual, emptyStringIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamEmptyNameReturnsEmpty", testCases[1].name) + } + + @Test + fun emptyStringAndNegativeIntQueryParamsAreAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val queryParamsIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getStringQueryParam("name"), getIntegerQueryParam("limit")))) + ensureGeneValue(queryParamsIndividual, "name", "") + ensureGeneValue(queryParamsIndividual, "limit", "-1") + + val solution = Solution(mutableListOf(simpleIndividual, queryParamsIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamsNegativeLimitEmptyNameReturnsEmpty", testCases[1].name) } private fun getPathParam(paramName: String): Param { @@ -245,6 +277,10 @@ class TestCaseDisambiguationTest { return QueryParam(paramName, OptionalGene(paramName, BooleanGene(paramName))) } + private fun getIntegerQueryParam(paramName: String): Param { + return QueryParam(paramName, OptionalGene(paramName, IntegerGene(paramName))) + } + /* Since the randomization used to construct the evaluated individuals might set a random boolean value, we do this to ensure the one we want for unit testing From 937a54aac7c203bd45aee01bd0795743795f586b Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Sun, 8 Dec 2024 22:02:28 -0300 Subject: [PATCH 07/15] Removed test logger --- .../core/output/naming/RestActionTestCaseNamingStrategy.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 1e7fd9230a..8fed9889d9 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -16,8 +16,6 @@ import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.NumberGene import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.string.StringGene -import org.slf4j.Logger -import org.slf4j.LoggerFactory import javax.ws.rs.core.MediaType open class RestActionTestCaseNamingStrategy( @@ -26,10 +24,6 @@ open class RestActionTestCaseNamingStrategy( private val nameWithQueryParameters: Boolean, ) : ActionTestCaseNamingStrategy(solution, languageConventionFormatter) { - companion object { - private val log: Logger = LoggerFactory.getLogger(RestActionTestCaseNamingStrategy::class.java) - } - override fun expandName(individual: EvaluatedIndividual<*>, nameTokens: MutableList, ambiguitySolvers: List<(Action) -> List>): String { val evaluatedAction = individual.evaluatedMainActions().last() val action = evaluatedAction.action as RestCallAction From d3138d41b3775e33b25ce33509803774fbf60ed5 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Mon, 9 Dec 2024 09:23:26 -0300 Subject: [PATCH 08/15] fixed javadoc --- .../org/evomaster/core/output/naming/TestCaseNamingStrategy.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt index b6bba292d3..6ef136df7c 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/TestCaseNamingStrategy.kt @@ -28,7 +28,7 @@ abstract class TestCaseNamingStrategy( /** * @param individual containing information for the test about to be named * @param nameTokens list to collect the identifiers which will be formatted into the test case name - * @param ambiguitySolver function receiving an action and returning a list of strings that will be added to the test case name + * @param ambiguitySolvers list of functions receiving an action and returning a list of strings that will be added to the test case name * * @return a String with extra information that will be included in the test name, regarding the EvaluatedIndividual */ From b9e9d472a64771212394e3fb4f8f24a6b43e28f0 Mon Sep 17 00:00:00 2001 From: Pgarrett Date: Mon, 9 Dec 2024 09:30:30 -0300 Subject: [PATCH 09/15] Added TODO for non OptionalGene wrapping --- .../core/output/naming/RestActionTestCaseNamingStrategy.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 8fed9889d9..a6774ebe03 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -201,6 +201,7 @@ open class RestActionTestCaseNamingStrategy( } } + // TODO: need to check if the BooleanGene is not enclosed in an OptionalGene private fun getBooleanQueryParams(queryParams: List): List { return queryParams.filter { val wrappedGene = getWrappedGene(it) @@ -208,6 +209,7 @@ open class RestActionTestCaseNamingStrategy( } } + // TODO: need to check if the NumberGene is not enclosed in an OptionalGene private fun getNegativeNumberQueryParams(queryParams: List): List { return queryParams.filter { val wrappedGene = getWrappedGene(it) @@ -215,6 +217,7 @@ open class RestActionTestCaseNamingStrategy( } } + // TODO: need to check if the StringGene is not enclosed in an OptionalGene private fun getEmptyStringQueryParams(queryParams: List): List { return queryParams.filter { val wrappedGene = getWrappedGene(it) From e637061636dc6d22a793b5d69cf2371e15786c18 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Mon, 16 Dec 2024 20:40:17 -0300 Subject: [PATCH 10/15] staticCheckIfImpactPhenotype for gene extraction --- .../RestActionTestCaseNamingStrategy.kt | 12 +++++-- .../naming/TestCaseDisambiguationTest.kt | 33 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index a6774ebe03..d45379104e 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -154,7 +154,8 @@ open class RestActionTestCaseNamingStrategy( restAction.path.getOnlyQuery(restAction.parameters) } .filter { it.value.isNotEmpty() && it.key.isNotEmpty()} - .flatMap { it.value }.toList() + .flatMap { it.value } + .toList() } /* @@ -225,8 +226,13 @@ open class RestActionTestCaseNamingStrategy( } } - private fun getWrappedGene(queryParam: QueryParam): Gene { - return (queryParam.getGeneForQuery() as OptionalGene).gene + private fun getWrappedGene(queryParam: QueryParam): Gene? { + val gene = queryParam.getGeneForQuery() + if (gene.staticCheckIfImpactPhenotype()) { + return gene + + } + return gene.getWrappedGene(OptionalGene::class.java)?.gene } private fun isGetCall(evaluatedAction: EvaluatedAction): Boolean { diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index 1dacb1a021..adfc546f4a 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -13,6 +13,7 @@ import org.evomaster.core.problem.rest.param.QueryParam import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Solution import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.gene.optional.OptionalGene @@ -265,20 +266,44 @@ class TestCaseDisambiguationTest { assertEquals("test_1_getOnLanguagesWithQueryParamsNegativeLimitEmptyNameReturnsEmpty", testCases[1].name) } + @Test + fun unwrappedNegativeNumberQueryParamIsAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val negativeQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getIntegerQueryParam("limit", false)))) + ensureGeneValue(negativeQPIndividual, "limit", "-1") + + val solution = Solution(mutableListOf(simpleIndividual, negativeQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamNegativeLimitReturnsEmpty", testCases[1].name) + } + private fun getPathParam(paramName: String): Param { return PathParam(paramName, CustomMutationRateGene(paramName, StringGene(paramName), 1.0)) } private fun getStringQueryParam(paramName: String): Param { - return QueryParam(paramName, OptionalGene(paramName, StringGene(paramName))) + return getQueryParam(paramName, StringGene(paramName)) } private fun getBooleanQueryParam(paramName: String): Param { - return QueryParam(paramName, OptionalGene(paramName, BooleanGene(paramName))) + return getQueryParam(paramName, BooleanGene(paramName)) + } + + private fun getIntegerQueryParam(paramName: String, wrapped: Boolean = true): Param { + return getQueryParam(paramName, IntegerGene(paramName), wrapped) + } + + private fun getQueryParam(paramName: String, gene: Gene, wrapped: Boolean = true): Param { + return QueryParam(paramName, if (wrapped) getWrappedGene(paramName, gene) else gene) } - private fun getIntegerQueryParam(paramName: String): Param { - return QueryParam(paramName, OptionalGene(paramName, IntegerGene(paramName))) + private fun getWrappedGene(paramName: String, gene: Gene): OptionalGene { + return OptionalGene(paramName, gene) } /* From 9686974d1d3ce72c5fc8bf09344ddf9e4caeb497 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Thu, 19 Dec 2024 21:42:29 -0300 Subject: [PATCH 11/15] Filter by keeping query params which have impacting phenotype genes --- .../naming/RestActionTestCaseNamingStrategy.kt | 10 +++------- .../output/naming/TestCaseDisambiguationTest.kt | 16 ---------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index d45379104e..82dde264f8 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -166,6 +166,7 @@ open class RestActionTestCaseNamingStrategy( val result = mutableListOf() val queryParams = restAction.path.getOnlyQuery(restAction.parameters) + .filter { it.getGeneForQuery().staticCheckIfImpactPhenotype() } result.add(with) result.add(if (queryParams.size > 1) "${queryParam}s" else queryParam) if (nameWithQueryParameters) { @@ -226,13 +227,8 @@ open class RestActionTestCaseNamingStrategy( } } - private fun getWrappedGene(queryParam: QueryParam): Gene? { - val gene = queryParam.getGeneForQuery() - if (gene.staticCheckIfImpactPhenotype()) { - return gene - - } - return gene.getWrappedGene(OptionalGene::class.java)?.gene + private fun getWrappedGene(queryParam: QueryParam): Gene { + return (queryParam.getGeneForQuery() as OptionalGene).gene } private fun isGetCall(evaluatedAction: EvaluatedAction): Boolean { diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index adfc546f4a..3f8b6c50b2 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -266,22 +266,6 @@ class TestCaseDisambiguationTest { assertEquals("test_1_getOnLanguagesWithQueryParamsNegativeLimitEmptyNameReturnsEmpty", testCases[1].name) } - @Test - fun unwrappedNegativeNumberQueryParamIsAdded() { - val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) - val negativeQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getIntegerQueryParam("limit", false)))) - ensureGeneValue(negativeQPIndividual, "limit", "-1") - - val solution = Solution(mutableListOf(simpleIndividual, negativeQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) - - val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) - - val testCases = namingStrategy.getTestCases() - assertEquals(2, testCases.size) - assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) - assertEquals("test_1_getOnLanguagesWithQueryParamNegativeLimitReturnsEmpty", testCases[1].name) - } - private fun getPathParam(paramName: String): Param { return PathParam(paramName, CustomMutationRateGene(paramName, StringGene(paramName), 1.0)) } From 6075f930e506c7c1ee887605a2c246689f22b2a1 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Thu, 19 Dec 2024 23:10:51 -0300 Subject: [PATCH 12/15] Use getWrappedGene instead of as --- .../core/output/naming/RestActionTestCaseNamingStrategy.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 82dde264f8..d6df69966f 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -227,8 +227,8 @@ open class RestActionTestCaseNamingStrategy( } } - private fun getWrappedGene(queryParam: QueryParam): Gene { - return (queryParam.getGeneForQuery() as OptionalGene).gene + private fun getWrappedGene(queryParam: QueryParam): Gene? { + return queryParam.getGeneForQuery().getWrappedGene(OptionalGene::class.java)?.gene } private fun isGetCall(evaluatedAction: EvaluatedAction): Boolean { From 1fd5f43c21a6c541f2d68dc104aa1775cd7c3ea6 Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Fri, 20 Dec 2024 22:39:53 -0300 Subject: [PATCH 13/15] Boolean and String gene unwrapped for test case naming --- .../RestActionTestCaseNamingStrategy.kt | 24 ++++++------- .../evomaster/core/problem/rest/RestPath.kt | 4 +-- .../naming/TestCaseDisambiguationTest.kt | 36 +++++++++++++++++-- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index d6df69966f..9d8ac0c6e7 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -85,7 +85,7 @@ open class RestActionTestCaseNamingStrategy( * When two or more individuals share a name, no disambiguation is performed. * Otherwise, we would just be increasing test case name length without having actually disambiguated. */ - private fun collectSolvedNames(ambiguitySolversPerIndividual: MutableMap, MutableList<(Action) -> List>>): Map, String> { + private fun collectSolvedNames(ambiguitySolversPerIndividual: Map, MutableList<(Action) -> List>>): Map, String> { return ambiguitySolversPerIndividual .map { it.key to expandName(it.key, mutableListOf(), it.value) } .groupBy({ it.second }, { it.first }) @@ -151,7 +151,8 @@ open class RestActionTestCaseNamingStrategy( return duplicatedIndividuals .groupBy { val restAction = it.evaluatedMainActions().last().action as RestCallAction - restAction.path.getOnlyQuery(restAction.parameters) + restAction.path.getOnlyUsableQueries(restAction.parameters) + .filter { queryParam -> queryParam.getGeneForQuery().staticCheckIfImpactPhenotype() } } .filter { it.value.isNotEmpty() && it.key.isNotEmpty()} .flatMap { it.value } @@ -165,7 +166,7 @@ open class RestActionTestCaseNamingStrategy( val restAction = action as RestCallAction val result = mutableListOf() - val queryParams = restAction.path.getOnlyQuery(restAction.parameters) + val queryParams = restAction.path.getOnlyUsableQueries(restAction.parameters) .filter { it.getGeneForQuery().staticCheckIfImpactPhenotype() } result.add(with) result.add(if (queryParams.size > 1) "${queryParam}s" else queryParam) @@ -206,31 +207,26 @@ open class RestActionTestCaseNamingStrategy( // TODO: need to check if the BooleanGene is not enclosed in an OptionalGene private fun getBooleanQueryParams(queryParams: List): List { return queryParams.filter { - val wrappedGene = getWrappedGene(it) - wrappedGene is BooleanGene && wrappedGene.value + val booleanGene = it.getGeneForQuery().getWrappedGene(BooleanGene::class.java) + booleanGene != null && booleanGene.staticCheckIfImpactPhenotype() && booleanGene.value } } // TODO: need to check if the NumberGene is not enclosed in an OptionalGene private fun getNegativeNumberQueryParams(queryParams: List): List { return queryParams.filter { - val wrappedGene = getWrappedGene(it) - wrappedGene is NumberGene<*> && wrappedGene.value.toLong() < 0 + val numberGene = it.getGeneForQuery().getWrappedGene(NumberGene::class.java) + numberGene != null && numberGene.staticCheckIfImpactPhenotype() && numberGene.value.toLong() < 0 } } - // TODO: need to check if the StringGene is not enclosed in an OptionalGene private fun getEmptyStringQueryParams(queryParams: List): List { return queryParams.filter { - val wrappedGene = getWrappedGene(it) - wrappedGene is StringGene && wrappedGene.getValueAsRawString().trim().isEmpty() + val stringGene = it.getGeneForQuery().getWrappedGene(StringGene::class.java) + stringGene != null && stringGene.staticCheckIfImpactPhenotype() && stringGene.getValueAsRawString().trim().isEmpty() } } - private fun getWrappedGene(queryParam: QueryParam): Gene? { - return queryParam.getGeneForQuery().getWrappedGene(OptionalGene::class.java)?.gene - } - private fun isGetCall(evaluatedAction: EvaluatedAction): Boolean { return (evaluatedAction.action as RestCallAction).verb == HttpVerb.GET } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt index f06c7882e5..dfe6ef381d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestPath.kt @@ -297,14 +297,14 @@ class RestPath(path: String) { return params.filter(usableQueryParamsFunction()).size } - fun getOnlyQuery(params: List): List { + fun getOnlyUsableQueries(params: List): List { return params .filter(usableQueryParamsFunction()) .filterIsInstance() } fun resolveOnlyQuery(params: List): List { - return getOnlyQuery(params) + return getOnlyUsableQueries(params) .map { q -> val name = encode(q.name) diff --git a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt index 3f8b6c50b2..8d1048a76f 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt @@ -266,12 +266,44 @@ class TestCaseDisambiguationTest { assertEquals("test_1_getOnLanguagesWithQueryParamsNegativeLimitEmptyNameReturnsEmpty", testCases[1].name) } + @Test + fun unwrappedNegativeNumberQueryParamIsAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val negativeQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getIntegerQueryParam("limit", false)))) + ensureGeneValue(negativeQPIndividual, "limit", "-1") + + val solution = Solution(mutableListOf(simpleIndividual, negativeQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamNegativeLimitReturnsEmpty", testCases[1].name) + } + + @Test + fun unwrappedEmptyStringQueryParamIsAdded() { + val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages")) + val emptyQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/languages", parameters = mutableListOf(getStringQueryParam("name", false)))) + ensureGeneValue(emptyQPIndividual, "name", "") + + val solution = Solution(mutableListOf(simpleIndividual, emptyQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList()) + + val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME) + + val testCases = namingStrategy.getTestCases() + assertEquals(2, testCases.size) + assertEquals("test_0_getOnLanguagesReturnsEmpty", testCases[0].name) + assertEquals("test_1_getOnLanguagesWithQueryParamEmptyNameReturnsEmpty", testCases[1].name) + } + private fun getPathParam(paramName: String): Param { return PathParam(paramName, CustomMutationRateGene(paramName, StringGene(paramName), 1.0)) } - private fun getStringQueryParam(paramName: String): Param { - return getQueryParam(paramName, StringGene(paramName)) + private fun getStringQueryParam(paramName: String, wrapped: Boolean = true): Param { + return getQueryParam(paramName, StringGene(paramName), wrapped) } private fun getBooleanQueryParam(paramName: String): Param { From e5439c1e605fe06d2c8bdd828ffa149f7027ecde Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 23 Dec 2024 09:43:43 +0100 Subject: [PATCH 14/15] supporting super-type handling in getWrappedGene --- .../kotlin/org/evomaster/core/search/gene/Gene.kt | 11 +++++++++-- .../evomaster/core/search/gene/optional/ChoiceGene.kt | 4 ++-- .../search/gene/optional/CustomMutationRateGene.kt | 4 ++-- .../core/search/gene/optional/FlexibleGene.kt | 4 ++-- .../search/gene/optional/SelectableWrapperGene.kt | 4 ++-- 5 files changed, 17 insertions(+), 10 deletions(-) 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 2998c4320e..5877677b19 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 @@ -300,15 +300,22 @@ abstract class Gene( * Wrapper genes, and only those, will override this method to check their children */ @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER") - open fun getWrappedGene(klass: Class) : T? where T : Gene, T : K{ + open fun getWrappedGene(klass: Class, strict: Boolean = false) : T? where T : Gene, T : K{ - if(this.javaClass == klass){ + if(matchingClass(klass,strict)){ return this as T } return null } + protected fun matchingClass(klass: Class<*>, strict: Boolean) : Boolean{ + if(strict){ + return this.javaClass == klass + } + return klass.isAssignableFrom(this.javaClass) + } + /** * there might be a need to repair gene based on some constraints, e.g., DateGene and TimeGene * diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/ChoiceGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/ChoiceGene.kt index eaf1c466ee..a9548bdc10 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/ChoiceGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/ChoiceGene.kt @@ -96,8 +96,8 @@ class ChoiceGene( } @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER") - override fun getWrappedGene(klass: Class) : T? where T : Gene, T: K{ - if(this.javaClass == klass){ + override fun getWrappedGene(klass: Class, strict: Boolean) : T? where T : Gene, T: K{ + if(matchingClass(klass,strict)){ return this as T } return activeGene().getWrappedGene(klass) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/CustomMutationRateGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/CustomMutationRateGene.kt index e37479d89e..f2cf4623e1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/CustomMutationRateGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/CustomMutationRateGene.kt @@ -57,8 +57,8 @@ class CustomMutationRateGene( } @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER") - override fun getWrappedGene(klass: Class) : T? where T : Gene, T: K{ - if(this.javaClass == klass){ + override fun getWrappedGene(klass: Class, strict: Boolean) : T? where T : Gene, T: K{ + if(matchingClass(klass,strict)){ return this as T } return gene.getWrappedGene(klass) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/FlexibleGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/FlexibleGene.kt index 574203e16a..839f62eb21 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/FlexibleGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/FlexibleGene.kt @@ -76,8 +76,8 @@ class FlexibleGene(name: String, } @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER") - override fun getWrappedGene(klass: Class) : T? where T : Gene, T: K{ - if(this.javaClass == klass){ + override fun getWrappedGene(klass: Class, strict: Boolean) : T? where T : Gene, T: K{ + if(matchingClass(klass,strict)){ return this as T } return gene.getWrappedGene(klass) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/SelectableWrapperGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/SelectableWrapperGene.kt index e3297e828e..6b73e6952f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/optional/SelectableWrapperGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/optional/SelectableWrapperGene.kt @@ -36,8 +36,8 @@ abstract class SelectableWrapperGene(name: String, } @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER") - override fun getWrappedGene(klass: Class) : T? where T : Gene, T: K{ - if(this.javaClass == klass){ + override fun getWrappedGene(klass: Class, strict: Boolean) : T? where T : Gene, T: K{ + if(matchingClass(klass,strict)){ return this as T } return gene.getWrappedGene(klass) From 94b090dabcdb5b3ce177b22e34c35169f59dd94e Mon Sep 17 00:00:00 2001 From: Philip Garrett Date: Mon, 23 Dec 2024 11:46:29 -0300 Subject: [PATCH 15/15] removed fixed TODOs --- .../core/output/naming/RestActionTestCaseNamingStrategy.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt index 9d8ac0c6e7..4a6a6b9d99 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/naming/RestActionTestCaseNamingStrategy.kt @@ -204,7 +204,6 @@ open class RestActionTestCaseNamingStrategy( } } - // TODO: need to check if the BooleanGene is not enclosed in an OptionalGene private fun getBooleanQueryParams(queryParams: List): List { return queryParams.filter { val booleanGene = it.getGeneForQuery().getWrappedGene(BooleanGene::class.java) @@ -212,7 +211,6 @@ open class RestActionTestCaseNamingStrategy( } } - // TODO: need to check if the NumberGene is not enclosed in an OptionalGene private fun getNegativeNumberQueryParams(queryParams: List): List { return queryParams.filter { val numberGene = it.getGeneForQuery().getWrappedGene(NumberGene::class.java)