diff --git a/core-it/pom.xml b/core-it/pom.xml index 14c4fe2da7..7ff4625e64 100644 --- a/core-it/pom.xml +++ b/core-it/pom.xml @@ -85,6 +85,29 @@ org.springframework.boot spring-boot-starter-web + + org.springdoc + springdoc-openapi-ui + + + + + + + org.springdoc + springdoc-openapi-kotlin + + + io.swagger.core.v3 + swagger-annotations + + + + org.evomaster + evomaster-e2e-tests-utils + test-jar + ${project.version} + diff --git a/core-it/src/main/kotlin/bar/examples/it/spring/pathstatus/PathStatusApplication.kt b/core-it/src/main/kotlin/bar/examples/it/spring/pathstatus/PathStatusApplication.kt new file mode 100644 index 0000000000..cf487e2d73 --- /dev/null +++ b/core-it/src/main/kotlin/bar/examples/it/spring/pathstatus/PathStatusApplication.kt @@ -0,0 +1,39 @@ +package bar.examples.it.spring.pathstatus + +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.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/pathstatus"]) +@RestController +open class PathStatusApplication { + + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(PathStatusApplication::class.java, *args) + } + } + + @GetMapping("/byStatus/{status}") + open fun getByStatus(@PathVariable status: Int) : ResponseEntity { + + return ResponseEntity.status(status).body("byStatus") + } + + @GetMapping("/others/{x}") + open fun getOthers(@PathVariable x: Int) : ResponseEntity { + + return ResponseEntity.status(200).body("$x") + } + + + +} \ No newline at end of file diff --git a/core-it/src/test/kotlin/bar/examples/it/spring/SpringController.kt b/core-it/src/test/kotlin/bar/examples/it/spring/SpringController.kt new file mode 100644 index 0000000000..4e39710cd6 --- /dev/null +++ b/core-it/src/test/kotlin/bar/examples/it/spring/SpringController.kt @@ -0,0 +1,67 @@ +package bar.examples.it.spring + +import org.evomaster.client.java.controller.EmbeddedSutController +import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto +import org.evomaster.client.java.controller.api.dto.SutInfoDto +import org.evomaster.client.java.sql.DbSpecification +import org.evomaster.client.java.controller.problem.ProblemInfo +import org.evomaster.client.java.controller.problem.RestProblem +import org.springframework.boot.SpringApplication +import org.springframework.context.ConfigurableApplicationContext + + +abstract class SpringController(protected val applicationClass: Class<*>) : EmbeddedSutController() { + + init { + super.setControllerPort(0) + } + + + protected var ctx: ConfigurableApplicationContext? = null + + override fun startSut(): String { + ctx = SpringApplication.run(applicationClass, "--server.port=0") + return "http://localhost:$sutPort" + } + + protected val sutPort: Int + get() = (ctx!!.environment + .propertySources["server.ports"].source as Map<*, *>)["local.server.port"] as Int + + override fun isSutRunning(): Boolean { + return ctx != null && ctx!!.isRunning + } + + override fun stopSut() { + ctx?.stop() + ctx?.close() + } + + override fun getPackagePrefixesToCover(): String { + return "bar.foo." + } + + override fun resetStateOfSUT() { //nothing to do + } + + override fun getProblemInfo(): ProblemInfo { + return RestProblem( + "http://localhost:$sutPort/v3/api-docs", + null + ) + } + + override fun getInfoForAuthentication(): List { + return listOf() + } + + override fun getDbSpecifications(): MutableList? { + return null + } + + + override fun getPreferredOutputFormat(): SutInfoDto.OutputFormat { + return SutInfoDto.OutputFormat.KOTLIN_JUNIT_5 + } + +} \ No newline at end of file diff --git a/core-it/src/test/kotlin/bar/examples/it/spring/pathstatus/PathStatusController.kt b/core-it/src/test/kotlin/bar/examples/it/spring/pathstatus/PathStatusController.kt new file mode 100644 index 0000000000..0ccd1dc97a --- /dev/null +++ b/core-it/src/test/kotlin/bar/examples/it/spring/pathstatus/PathStatusController.kt @@ -0,0 +1,5 @@ +package bar.examples.it.spring.pathstatus + +import bar.examples.it.spring.SpringController + +class PathStatusController : SpringController(PathStatusApplication::class.java) \ No newline at end of file diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/IntegrationTestRestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/IntegrationTestRestBase.kt new file mode 100644 index 0000000000..3f1f2feff9 --- /dev/null +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/IntegrationTestRestBase.kt @@ -0,0 +1,50 @@ +package org.evomaster.core.problem.rest + +import com.google.inject.Injector +import org.evomaster.core.problem.enterprise.SampleType +import org.evomaster.core.problem.rest.service.AbstractRestFitness +import org.evomaster.core.problem.rest.service.AbstractRestSampler +import org.evomaster.core.search.EvaluatedIndividual +import org.evomaster.core.search.service.SearchGlobalState +import org.evomaster.core.seeding.service.rest.PirToRest +import org.evomaster.e2etests.utils.RestTestBase +import org.junit.jupiter.api.BeforeEach + +abstract class IntegrationTestRestBase : RestTestBase() { + + + protected lateinit var injector: Injector + + @BeforeEach + fun initInjector(){ + val args = listOf( + "--sutControllerPort", "" + controllerPort, + "--createConfigPathIfMissing", "false" + ) + injector = init(args) + } + + fun getPirToRest() = injector.getInstance(PirToRest::class.java) + + + fun createIndividual(actions: List): EvaluatedIndividual { + +// val searchGlobalState = injector.getInstance(SearchGlobalState::class.java) + +// val ind = RestIndividual(actions.toMutableList(), SampleType.SEEDED) +// ind.doGlobalInitialize(searchGlobalState) + + val sampler = injector.getInstance(AbstractRestSampler::class.java) + /* + the method `createIndividual` can be overridden + in this case, the sampler is an instance of ResourceSampler, + then check its implementation in ResourceSampler.createIndividual(...) + */ + val ind = sampler.createIndividual(SampleType.SEEDED, actions.toMutableList()) + + val ff = injector.getInstance(AbstractRestFitness::class.java) + val ei = ff.calculateCoverage(ind)!! + + return ei + } +} \ No newline at end of file diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/selectorutils/RestIndividualSelectorUtilsPathStatusTest.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/selectorutils/RestIndividualSelectorUtilsPathStatusTest.kt new file mode 100644 index 0000000000..6c34662a22 --- /dev/null +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/selectorutils/RestIndividualSelectorUtilsPathStatusTest.kt @@ -0,0 +1,50 @@ +package org.evomaster.core.problem.rest.selectorutils + +import bar.examples.it.spring.pathstatus.PathStatusController +import org.evomaster.core.problem.rest.HttpVerb +import org.evomaster.core.problem.rest.IntegrationTestRestBase +import org.evomaster.core.problem.rest.RestIndividualSelectorUtils +import org.evomaster.core.problem.rest.RestPath +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class RestIndividualSelectorUtilsPathStatusTest : IntegrationTestRestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(PathStatusController()) + } + } + + + @Test + fun testPathStatus(){ + + val pirTest = getPirToRest() + + val byStatus = RestPath("/api/pathstatus/byStatus/{status}") + val others = RestPath("/api/pathstatus/others/{x}") + + val s200 = pirTest.fromVerbPath("get", "/api/pathstatus/byStatus/200")!! + val s400 = pirTest.fromVerbPath("get", "/api/pathstatus/byStatus/400")!! + val o200 = pirTest.fromVerbPath("get", "/api/pathstatus/others/200")!! + val o500 = pirTest.fromVerbPath("get", "/api/pathstatus/others/500")!! + + val x0 = createIndividual(listOf(s200)) + val x1 = createIndividual(listOf(s400)) + val x2 = createIndividual(listOf(o200)) + val x3 = createIndividual(listOf(o500)) + + val individuals = listOf(x0,x1,x2,x3) + + val r0 = RestIndividualSelectorUtils.findIndividuals(individuals, HttpVerb.GET, byStatus, 200) + assertEquals(1, r0.size) + + val r1 = RestIndividualSelectorUtils.findIndividuals(individuals, HttpVerb.GET, byStatus, 500) + assertEquals(0, r1.size) + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/param/Param.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/param/Param.kt index 84e5e48e7a..296837c82e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/param/Param.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/param/Param.kt @@ -9,7 +9,7 @@ abstract class Param( val genes : MutableList ) : StructuralElement(genes){ - //TODO need refactoring + //TODO need refactoring. eg shared abstract class for cases in which only 1 gene for sure @Deprecated("Assumes there is only 1 gene") val gene : Gene = genes[0] diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index c784678a88..d35fa3a4cc 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -28,6 +28,9 @@ import kotlin.math.max * * @property trackOperator indicates which operator create this individual. * @property index indicates when the individual is created, ie, using num of evaluated individual. + * + * + * FIXME: why having to use AbstractRestSampler.createIndividual() instead of having such code here in constructor??? */ class RestIndividual( sampleType: SampleType, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividualSelectorUtils.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividualSelectorUtils.kt index 796ca92c66..431be6559a 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividualSelectorUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividualSelectorUtils.kt @@ -52,7 +52,7 @@ object RestIndividualSelectorUtils { return null } - fun findIndividuals( + fun old_findIndividuals( individuals: List>, verb: HttpVerb, statusGroup: String @@ -88,9 +88,11 @@ object RestIndividualSelectorUtils { /* Find individuals containing a certain action and STATUS */ - fun getIndividualsWithActionAndStatus(individualsInSolution: List>, - verb: HttpVerb, statusCode: Int) - :List> { + fun getIndividualsWithActionAndStatus( + individualsInSolution: List>, + verb: HttpVerb, + statusCode: Int + ):List> { val individualsList = mutableListOf>() @@ -143,7 +145,7 @@ object RestIndividualSelectorUtils { return foundRestAction } - fun getIndividualsWithActionAndStatus( + fun findIndividuals( individuals: List>, verb: HttpVerb, path: RestPath, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/AbstractParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/AbstractParser.kt index 27594560fb..c758342c46 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/AbstractParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/AbstractParser.kt @@ -28,6 +28,7 @@ import java.util.regex.Pattern * the set of default actions (i.e., actions representing single calls to each API * operation) and the Swagger specification. */ +@Deprecated("Code here will be replaced with intermediate representation. See new 'seeding' package") abstract class AbstractParser( protected val defaultRestCallActions: List, protected val swagger: OpenAPI diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/Parser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/Parser.kt index 90934b759f..8bc09ecba7 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/Parser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/Parser.kt @@ -2,6 +2,8 @@ package org.evomaster.core.problem.rest.seeding import org.evomaster.core.problem.rest.RestCallAction + +@Deprecated("Code here will be replaced with intermediate representation. See new 'seeding' package") interface Parser { /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParser.kt index 1069f5d4db..8c1591f74c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParser.kt @@ -17,6 +17,7 @@ import java.lang.IllegalStateException import java.net.URLDecoder import java.nio.charset.StandardCharsets +@Deprecated("Code here will be replaced with intermediate representation. See new 'seeding' package") class PostmanParser( defaultRestCallActions: List, swagger: OpenAPI diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt index 6df3f365a6..e6add341ff 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt @@ -49,7 +49,7 @@ import javax.ws.rs.core.NewCookie import javax.ws.rs.core.Response -abstract class AbstractRestFitness : HttpWsFitness() where T : Individual { +abstract class AbstractRestFitness : HttpWsFitness() { // TODO: This will moved under ApiWsFitness once RPC and GraphQL support is completed @Inject @@ -760,7 +760,7 @@ abstract class AbstractRestFitness : HttpWsFitness() where T : Individual return null } - val dto = updateFitnessAfterEvaluation(targets, allCovered, individual as T, fv) + val dto = updateFitnessAfterEvaluation(targets, allCovered, individual, fv) ?: return null handleExtra(dto, fv) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 87fef95987..66b6194c6b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -307,6 +307,9 @@ abstract class AbstractRestSampler : HttpWsSampler() { /** * @return a created individual with specified actions, i.e., [restCalls]. * All actions must have been already initialized + * + * + * FIXME: why this function instead of dealing with this directly in the constructor of RestIndividual??? */ open fun createIndividual(sampleType: SampleType, restCalls: MutableList): RestIndividual { if(restCalls.any { !it.isInitialized() }){ diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt index 7986c3d743..28463dfd57 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt @@ -12,6 +12,7 @@ import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler +import org.evomaster.core.seeding.service.rest.PirToRest class BlackBoxRestModule( val usingRemoteController: Boolean @@ -69,5 +70,9 @@ class BlackBoxRestModule( bind(SecurityRest::class.java) .asEagerSingleton() + + bind(PirToRest::class.java) + .asEagerSingleton() + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt index 8ebd8d70bf..3b3852554d 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt @@ -17,6 +17,7 @@ import org.evomaster.core.search.service.Sampler import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StandardMutator import org.evomaster.core.search.service.mutator.StructureMutator +import org.evomaster.core.seeding.service.rest.PirToRest class ResourceRestModule(private val bindRemote : Boolean = true) : AbstractModule(){ @@ -59,7 +60,7 @@ class ResourceRestModule(private val bindRemote : Boolean = true) : AbstractModu .to(RestResourceFitness::class.java) .asEagerSingleton() - bind(object : TypeLiteral>() {}) + bind(object : TypeLiteral() {}) .to(RestResourceFitness::class.java) .asEagerSingleton() @@ -112,5 +113,8 @@ class ResourceRestModule(private val bindRemote : Boolean = true) : AbstractModu bind(SecurityRest::class.java) .asEagerSingleton() + bind(PirToRest::class.java) + .asEagerSingleton() + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 7e6e33a476..2a082c8dce 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -12,7 +12,7 @@ import org.evomaster.core.search.FitnessValue import org.slf4j.Logger import org.slf4j.LoggerFactory -open class RestFitness : AbstractRestFitness() { +open class RestFitness : AbstractRestFitness() { companion object { private val log: Logger = LoggerFactory.getLogger(RestFitness::class.java) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt index 9277f7c1c4..8ed74f46fd 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt @@ -14,6 +14,7 @@ import org.evomaster.core.search.service.mutator.StandardMutator import org.evomaster.core.search.service.* import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StructureMutator +import org.evomaster.core.seeding.service.rest.PirToRest class RestModule(private val bindRemote : Boolean = true) : AbstractModule(){ @@ -88,5 +89,7 @@ class RestModule(private val bindRemote : Boolean = true) : AbstractModule(){ bind(SecurityRest::class.java) .asEagerSingleton() + bind(PirToRest::class.java) + .asEagerSingleton() } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index 125d8f5516..b94c34b39d 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -23,7 +23,7 @@ import javax.ws.rs.core.NewCookie /** * take care of calculating/collecting fitness of [RestIndividual] */ -class RestResourceFitness : AbstractRestFitness() { +class RestResourceFitness : AbstractRestFitness() { @Inject diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/SecurityRest.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/SecurityRest.kt index f01196853f..975057e816 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/SecurityRest.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/SecurityRest.kt @@ -3,27 +3,16 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import javax.annotation.PostConstruct -import org.apache.http.HttpStatus - -import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto - import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.enterprise.auth.AuthSettings import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo import org.evomaster.core.problem.rest.* -import org.evomaster.core.problem.rest.param.PathParam -import org.evomaster.core.remote.service.RemoteController -import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.* -import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction import org.evomaster.core.search.service.Randomness -import org.evomaster.core.search.service.Sampler //FIXME this needs to be cleaned-up @@ -167,7 +156,7 @@ class SecurityRest { // from archive, search if there is any test with a DELETE returning a 403 val existing403 : List> = - RestIndividualSelectorUtils.getIndividualsWithActionAndStatus(individualsInSolution, HttpVerb.DELETE, delete.path, 403) + RestIndividualSelectorUtils.findIndividuals(individualsInSolution, HttpVerb.DELETE, delete.path, 403) var individualToChooseForTest : RestIndividual 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 39c7ea27dd..319fc1640f 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 @@ -767,6 +767,17 @@ abstract class Gene( open fun possiblySame(gene : Gene) : Boolean = gene.name == name && gene::class == this::class + /** + * Given a string value, apply it to the current state of this gene (and possibly recursively to its children). + * If it fails for any reason, return false, without modifying its state. + */ + open fun setFromStringValue(value: String) : Boolean{ + //TODO in future this should be abstract, to force each gene to handle it. + //few implementations can be based on AbstractParser class for Postman + throw IllegalStateException("setFromStringValue() is not implemented for gene ${this::class.simpleName}") + } + + //========================= handing binding genes =================================== //TODO make sure all public methods keep the gene in a valid state. diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt index 48b4f149fb..64e79b2af0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt @@ -52,6 +52,15 @@ class IntegerGene( private val log: Logger = LoggerFactory.getLogger(IntegerGene::class.java) } + override fun setFromStringValue(value: String) : Boolean{ + try{ + this.value = value.toInt() + return true + }catch (e: NumberFormatException){ + return false + } + } + override fun copyContent(): Gene { return IntegerGene( name, 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 867292e06b..d166aedadb 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 @@ -52,6 +52,10 @@ class CustomMutationRateGene( } + override fun setFromStringValue(value: String) : Boolean{ + return gene.setFromStringValue(value) + } + override fun getWrappedGene(klass: Class) : T? where T : Gene{ if(this.javaClass == klass){ return this as T diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt index aa692b5c9f..a297c9e982 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt @@ -69,7 +69,7 @@ class StringGene( init { if (minLength>maxLength) { - throw IllegalArgumentException("Cannot create string gene ${this.name} with mininum length ${this.minLength} and maximum length ${this.maxLength}") + throw IllegalArgumentException("Cannot create string gene ${this.name} with minimum length ${this.minLength} and maximum length ${this.maxLength}") } } @@ -983,4 +983,20 @@ class StringGene( return otherelements.none { it is Gene && it.flatView().any { g-> g is StringGene && g.getPossiblyTaintedValue().equals(getPossiblyTaintedValue(), ignoreCase = true) } } } + + override fun setFromStringValue(value: String): Boolean { + + val previousSpecialization = selectedSpecialization + val previousValue = value + + this.value = value + selectedSpecialization = -1 + if(!isLocallyValid() || !checkForGloballyValid()){ + this.value = previousValue + this.selectedSpecialization = previousSpecialization + return false + } + + return true + } } diff --git a/core/src/main/kotlin/org/evomaster/core/seeding/pir/PirTestCase.kt b/core/src/main/kotlin/org/evomaster/core/seeding/pir/PirTestCase.kt new file mode 100644 index 0000000000..9371cf7c9b --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/seeding/pir/PirTestCase.kt @@ -0,0 +1,4 @@ +package org.evomaster.core.seeding.pir + +class PirTestCase { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/seeding/service/PirToIndividual.kt b/core/src/main/kotlin/org/evomaster/core/seeding/service/PirToIndividual.kt new file mode 100644 index 0000000000..de6ff46eca --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/seeding/service/PirToIndividual.kt @@ -0,0 +1,25 @@ +package org.evomaster.core.seeding.service + +import com.google.inject.Inject +import org.evomaster.core.problem.rest.service.AbstractRestSampler +import org.evomaster.core.search.service.Randomness + +/** + * From Pojo Internal Representation (PIR), possibly derived from a textual representation (eg in JSON) of a test case, + * do create an individual, based on actual schema of the SUT. + * Note: we can assume that schema might change, and so the "old" test cases might not fully match the constraint + * in the current schema. Also, text might be manually modified by users. + * In other words, we can assume errors in parsing... in such cases, we should NOT crash EM, but rather issue warning + * messages. + * + * TODO should consider if this should be made a "service" + * + * TODO this will be the core of new "seeding" mechanism, eg seed from previous runs or manual edits from users, + * but for now we just need something basic to enable integration tests + */ +abstract class PirToIndividual { + + @Inject + protected lateinit var randomness: Randomness + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/seeding/service/rest/PirToRest.kt b/core/src/main/kotlin/org/evomaster/core/seeding/service/rest/PirToRest.kt new file mode 100644 index 0000000000..f50168e092 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/seeding/service/rest/PirToRest.kt @@ -0,0 +1,83 @@ +package org.evomaster.core.seeding.service.rest + +import com.google.inject.Inject +import org.evomaster.core.problem.enterprise.auth.AuthSettings +import org.evomaster.core.problem.rest.HttpVerb +import org.evomaster.core.problem.rest.RestCallAction +import org.evomaster.core.problem.rest.param.PathParam +import org.evomaster.core.problem.rest.service.AbstractRestSampler +import org.evomaster.core.search.service.Randomness +import org.evomaster.core.seeding.service.PirToIndividual +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import javax.annotation.PostConstruct + +class PirToRest: PirToIndividual(){ + + companion object{ + private val log: Logger = LoggerFactory.getLogger(PirToRest::class.java) + } + + @Inject + private lateinit var sampler: AbstractRestSampler + + + //TODO move up, once doing refactoring + private lateinit var authSettings: AuthSettings + + + /** + * All actions that can be defined from the OpenAPI schema + */ + private lateinit var actionDefinitions : List + + @PostConstruct + fun initialize() { + actionDefinitions = sampler.getActionDefinitions() as List + authSettings = sampler.authentications + } + + + fun fromVerbPath(verb: String, path: String) : RestCallAction?{ + + val v = try{HttpVerb.valueOf(verb.uppercase())} + catch (e: IllegalArgumentException){ + log.warn("Unrecognized http verb: $verb") + return null + } + + val candidates = actionDefinitions.filter { it.verb == v } + .filter { it.path.matches(path) } + + if(candidates.isEmpty()){ + log.warn("No match for endpoint path: $path") + return null + } + + if(candidates.size > 1){ + log.warn("Ambiguity issue, as ${candidates.size} endpoints are a match for: $path") + } + + val x = candidates[0].copy() as RestCallAction + x.doInitialize(randomness) + + x.parameters.forEach { p -> + when(p){ + is PathParam -> { + val toSeed = x.path.getKeyValues(path)?.get(p.name)!! + val isSet = p.gene.setFromStringValue(toSeed) + if(!isSet){ + log.warn("Failed to update path parameter ${p.name} with value: $toSeed") + return null + } + } + else -> { + //TODO other cases + } + } + } + + return x + } + +} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualDisabledHMTest.kt index d95816f3c1..7eb7152dfa 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualDisabledHMTest.kt @@ -40,7 +40,7 @@ class RestIndividualDisabledHMTest : RestIndividualTestBase(){ override fun getMutator(): StandardMutator = mutator - override fun getFitnessFunction(): AbstractRestFitness = ff + override fun getFitnessFunction(): AbstractRestFitness = ff } \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt index afc3b93f0f..540b1e0510 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt @@ -25,7 +25,7 @@ class RestIndividualResourceTest : RestIndividualTestBase() { override fun getProblemModule() = ResourceRestModule(false) override fun getMutator(): StandardMutator = mutator - override fun getFitnessFunction(): AbstractRestFitness = ff + override fun getFitnessFunction(): AbstractRestFitness = ff override fun getSampler(): AbstractRestSampler = sampler diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt index 4827c74e2b..a14ac059d1 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt @@ -175,7 +175,7 @@ abstract class RestIndividualTestBase { abstract fun getSampler() : AbstractRestSampler abstract fun getMutator() : StandardMutator - abstract fun getFitnessFunction() : AbstractRestFitness + abstract fun getFitnessFunction() : AbstractRestFitness @ParameterizedTest @MethodSource("getBudgetAndNumOfResourceForSampler") diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index cc5bbad676..11ee5993f7 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -80,7 +80,7 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ override fun getMutator(): StandardMutator = mutator - override fun getFitnessFunction(): AbstractRestFitness = ff + override fun getFitnessFunction(): AbstractRestFitness = ff private fun sampleDbAction(table : Table) : List{ val actions = sqlInsertBuilder!!.createSqlInsertionAction(table.name)