Skip to content

Commit ea8a580

Browse files
committed
EnumGene supported for DTO usage
1 parent ee8881c commit ea8a580

8 files changed

Lines changed: 161 additions & 8 deletions

File tree

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertRest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,14 @@ class DtoReflectiveAssertRest {
4444
return ResponseEntity.ok("OK")
4545
}
4646

47+
@PostMapping(path = ["/enum-examples"], consumes = [MediaType.APPLICATION_JSON_VALUE])
48+
open fun enumExamples(@RequestBody body: EnumExamplesDto) : ResponseEntity<String>{
49+
return ResponseEntity.ok("OK")
50+
}
51+
52+
@PostMapping(path = ["/enum-type"], consumes = [MediaType.APPLICATION_JSON_VALUE])
53+
open fun enumType(@RequestBody body: EnumTypeDto) : ResponseEntity<String>{
54+
return ResponseEntity.ok("OK")
55+
}
56+
4757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class EnumExamplesDto {
4+
5+
var textValue: String = ""
6+
var flagValue: Boolean = false
7+
var countValue: Int = -1
8+
var scoreValue: Float = -3.14f
9+
var listValue: List<String> = emptyList()
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class EnumTypeDto {
4+
5+
var name: String = ""
6+
var status: String = ""
7+
var intStatus: Int = -10
8+
9+
}

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-dto-reflective-assert.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,30 @@ paths:
166166
responses:
167167
'200':
168168
description: OK
169+
/enum-type:
170+
post:
171+
summary: Create an item using enums
172+
requestBody:
173+
required: true
174+
content:
175+
application/json:
176+
schema:
177+
$ref: '#/components/schemas/EnumType'
178+
responses:
179+
'200':
180+
description: OK
181+
/enum-examples:
182+
post:
183+
summary: Submit an object using examples
184+
requestBody:
185+
required: true
186+
content:
187+
application/json:
188+
schema:
189+
$ref: '#/components/schemas/EnumExample'
190+
responses:
191+
'200':
192+
description: OK
169193

170194
components:
171195
schemas:
@@ -211,3 +235,47 @@ components:
211235
required:
212236
- name
213237
- age
238+
239+
EnumType:
240+
type: object
241+
properties:
242+
name:
243+
type: string
244+
status:
245+
type: string
246+
enum: [ active, inactive, archived ]
247+
intStatus:
248+
type: integer
249+
enum: [ -1, 0, 1 ]
250+
required:
251+
- name
252+
- status
253+
- intStatus
254+
255+
EnumExample:
256+
type: object
257+
properties:
258+
textValue:
259+
type: string
260+
example: "hello world"
261+
flagValue:
262+
type: boolean
263+
example: true
264+
countValue:
265+
type: integer
266+
example: 33
267+
scoreValue:
268+
type: number
269+
format: float
270+
example: 3.14
271+
listValue:
272+
type: array
273+
items:
274+
type: string
275+
example: [ "s1", "s2", "s3" ]
276+
required:
277+
- textValue
278+
- flagValue
279+
- countValue
280+
- scoreValue
281+
- listValue

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertEMTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
4343
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/parent", "OK")
4444
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-inline", "OK")
4545
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-components", "OK")
46+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-type", "OK")
47+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-examples", "OK")
4648
}
4749

4850
assertPrimitiveTypeDtoCreated()
@@ -51,6 +53,8 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
5153
assertItemsInlineDtoCreated()
5254
assertAnyOfDtoCreated()
5355
assertOneOfDtoCreated()
56+
assertEnumTypeDtoCreated()
57+
assertEnumExampleDtoCreated()
5458
}
5559

5660
private fun assertPrimitiveTypeDtoCreated() {
@@ -107,6 +111,22 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
107111
assertProperty(klass, instance, "mouse", "Jerry")
108112
}
109113

114+
private fun assertEnumTypeDtoCreated() {
115+
val (klass, instance) = initDtoClass("EnumType")
116+
assertProperty(klass, instance, "name", "EvoMaster")
117+
assertProperty(klass, instance, "status", "active")
118+
assertProperty(klass, instance, "intStatus", 1)
119+
}
120+
121+
private fun assertEnumExampleDtoCreated() {
122+
val (klass, instance) = initDtoClass("EnumExample")
123+
assertProperty(klass, instance, "textValue", "A text value")
124+
assertProperty(klass, instance, "flagValue", true)
125+
assertProperty(klass, instance, "countValue", 33)
126+
assertProperty(klass, instance, "scoreValue", 3.14f)
127+
assertProperty(klass, instance, "listValue", mutableListOf("s1", "s2", "s3"))
128+
}
129+
110130
private fun initDtoClass(name: String): Pair<KClass<out Any>, Any> {
111131
val className = ClassName("org.foo.dto.$name")
112132
val klass = loadClass(className).kotlin

core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.evomaster.core.search.gene.BooleanGene
1111
import org.evomaster.core.search.gene.Gene
1212
import org.evomaster.core.search.gene.ObjectGene
1313
import org.evomaster.core.search.gene.collection.ArrayGene
14+
import org.evomaster.core.search.gene.collection.EnumGene
1415
import org.evomaster.core.search.gene.datetime.DateGene
1516
import org.evomaster.core.search.gene.datetime.DateTimeGene
1617
import org.evomaster.core.search.gene.datetime.TimeGene
@@ -93,7 +94,6 @@ class DtoWriter(
9394

9495

9596
private fun calculateDtoFromChoice(gene: ChoiceGene<*>, actionName: String) {
96-
// TODO: should we handle EnumGene?
9797
if (hasObjectOrArrayGene(gene)) {
9898
val dtoName = TestWriterUtils.safeVariableName(actionName)
9999
if (!dtoCollector.contains(dtoName)) {
@@ -143,16 +143,15 @@ class DtoWriter(
143143
val dtoName = TestWriterUtils.safeVariableName(gene.refType?:actionName)
144144
if (!dtoCollector.contains(dtoName)) {
145145
val dtoClass = DtoClass(dtoName)
146-
// TODO: add support for additionalFields
147146
populateDtoClass(dtoClass, gene)
148147
dtoCollector.put(dtoName, dtoClass)
149148
}
150149
}
151150

152151
private fun calculateDtoFromArray(gene: ArrayGene<*>, actionName: String) {
153152
val template = gene.template
154-
// TODO consider ChoiceGene. Primitive types won't be considered, an array of strings should not be wrapped
155-
// into a DTO but just use List<String> for setting the payload.
153+
// Primitive types won't be considered, an array of strings should not be wrapped
154+
// into a DTO but just use List<String> for setting the payload.
156155
if (template is ObjectGene) {
157156
calculateDtoFromObject(template, actionName)
158157
} else {
@@ -188,7 +187,6 @@ class DtoWriter(
188187

189188
private fun getDtoType(fieldName: String, field: Gene?): String {
190189
return when (field) {
191-
// TODO: handle nested arrays, objects and extend type system for dto fields
192190
is StringGene -> "String"
193191
is IntegerGene -> if (outputFormat.isJava()) "Integer" else "Int"
194192
is LongGene -> "Long"
@@ -203,6 +201,7 @@ class DtoWriter(
203201
is BooleanGene -> "Boolean"
204202
is ObjectGene -> field.refType?:StringUtils.capitalization(fieldName)
205203
is ArrayGene<*> -> if (outputFormat.isJava()) "List<${getDtoType(field.name, field.template)}>" else "MutableList<${getDtoType(field.name, field.template)}>"
204+
is EnumGene<*> -> field.getValueType(outputFormat.isKotlin())
206205
else -> throw Exception("Not supported gene at the moment: ${field?.javaClass?.simpleName} for field $fieldName")
207206
}
208207
}

core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.evomaster.core.search.gene.BooleanGene
66
import org.evomaster.core.search.gene.Gene
77
import org.evomaster.core.search.gene.ObjectGene
88
import org.evomaster.core.search.gene.collection.ArrayGene
9+
import org.evomaster.core.search.gene.collection.EnumGene
910
import org.evomaster.core.search.gene.datetime.DateGene
1011
import org.evomaster.core.search.gene.datetime.DateTimeGene
1112
import org.evomaster.core.search.gene.datetime.TimeGene
@@ -105,7 +106,13 @@ class GeneToDto(
105106
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, childDtoCall.varName))
106107
}
107108
else -> {
108-
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = null)}${getValueSuffix(leafGene)}"))
109+
if (leafGene is EnumGene<*> && it is ChoiceGene<*>) {
110+
val children = it.getViewOfChildren()
111+
val otherChoice = children.find { child -> child != leafGene }
112+
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = null)}${getValueSuffix(otherChoice)}"))
113+
} else {
114+
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = null)}${getValueSuffix(leafGene)}"))
115+
}
109116
}
110117
}
111118
}
@@ -164,7 +171,7 @@ class GeneToDto(
164171

165172
// According to documentation, a trailing constant is only needed for Long, Hexadecimal and Float
166173
// https://kotlinlang.org/docs/numbers.html#literal-constants-for-numbers
167-
private fun getValueSuffix(gene: Gene): String {
174+
private fun getValueSuffix(gene: Gene?): String {
168175
return when (gene) {
169176
is LongGene -> "L"
170177
is FloatGene -> "f"

core/src/main/kotlin/org/evomaster/core/search/gene/collection/EnumGene.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package org.evomaster.core.search.gene.collection
22

33
import org.evomaster.core.output.OutputFormat
4+
import org.evomaster.core.search.gene.BooleanGene
45
import org.evomaster.core.search.gene.Gene
56
import org.evomaster.core.search.gene.interfaces.NamedExamplesGene
7+
import org.evomaster.core.search.gene.numeric.DoubleGene
8+
import org.evomaster.core.search.gene.numeric.FloatGene
9+
import org.evomaster.core.search.gene.numeric.IntegerGene
10+
import org.evomaster.core.search.gene.numeric.LongGene
611
import org.evomaster.core.search.gene.string.StringGene
712
import org.evomaster.core.search.gene.root.SimpleGene
813
import org.evomaster.core.search.gene.utils.GeneUtils
14+
import org.evomaster.core.search.gene.wrapper.ChoiceGene
915
import org.evomaster.core.search.impact.impactinfocollection.value.collection.EnumGeneImpact
1016
import org.evomaster.core.search.service.AdaptiveParameterControl
1117
import org.evomaster.core.search.service.Randomness
@@ -197,6 +203,29 @@ class EnumGene<T : Comparable<T>>(
197203
return valueNames?.get(index)
198204
}
199205

206+
fun getValueType(isKotlinOutput: Boolean): String {
207+
return when {
208+
values.isEmpty() -> "String"
209+
parent is ChoiceGene<*> -> getTypeForExampleEnum(isKotlinOutput)
210+
values.first() is Int && isKotlinOutput -> "Int"
211+
else -> values.first().javaClass.simpleName
212+
}
213+
}
214+
215+
// In this case whe need to check the other leaf in the ChoiceGene to extract the example type
216+
private fun getTypeForExampleEnum(isKotlinOutput: Boolean): String {
217+
val children = (parent as ChoiceGene<*>).getViewOfChildren()
218+
val otherChoice = children.find { it != this }
219+
return when (otherChoice) {
220+
is IntegerGene -> if (isKotlinOutput) "Int" else "Integer"
221+
is LongGene -> "Long"
222+
is DoubleGene -> "Double"
223+
is FloatGene -> "Float"
224+
is BooleanGene -> "Boolean"
225+
else -> "String"
226+
}
227+
}
228+
200229
override fun copyValueFrom(other: Gene): Boolean {
201230
if (other !is EnumGene<*>) {
202231
throw IllegalArgumentException("Invalid gene type ${other.javaClass}")
@@ -243,4 +272,4 @@ class EnumGene<T : Comparable<T>>(
243272
index = target
244273
return true
245274
}
246-
}
275+
}

0 commit comments

Comments
 (0)