Skip to content

Commit f7f61af

Browse files
authored
Merge branch 'master' into phg/dto-handle-null
2 parents 439e423 + 05c5684 commit f7f61af

8 files changed

Lines changed: 269 additions & 27 deletions

File tree

core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ abstract class ApiWsStructureMutator : StructureMutator() {
399399
}
400400

401401
private fun <T : ApiWsIndividual> handleDSE(ind: T, sampler: ApiWsSampler<T>, failedWhereQueries: List<String>): MutableList<List<SqlAction>> {
402-
/* TODO: DSE should be plugged in here */
403402
val schemaDto = sampler.sqlInsertBuilder?.schemaDto
404403
?: throw IllegalStateException("No DB schema is available")
405404

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,10 @@ class ArrayGene<T>(
376376
*/
377377
fun doesExist(gene: T): Boolean{
378378
if (!isElementApplicableToUniqueCheck(ParamUtil.getValueGene(gene))) return false
379-
return elements.any { ParamUtil.getValueGene(it).containsSameValueAs(ParamUtil.getValueGene(gene)) }
379+
return elements.any {
380+
it.getLeafGene().javaClass == gene.getLeafGene().javaClass &&
381+
it.getLeafGene().containsSameValueAs(gene.getLeafGene())
382+
}
380383
}
381384

382385
/**

core/src/main/kotlin/org/evomaster/core/solver/SMTLibZ3DbConstraintSolver.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import org.evomaster.core.search.gene.BooleanGene
1515
import org.evomaster.core.search.gene.Gene
1616
import org.evomaster.core.search.gene.numeric.DoubleGene
1717
import org.evomaster.core.search.gene.numeric.IntegerGene
18-
import org.evomaster.core.search.gene.numeric.LongGene
18+
import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene
1919
import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene
2020
import org.evomaster.core.search.gene.string.StringGene
2121
import org.evomaster.core.sql.SqlAction
@@ -28,6 +28,10 @@ import java.io.IOException
2828
import java.nio.charset.StandardCharsets
2929
import java.nio.file.Files
3030
import java.nio.file.Paths
31+
import java.time.Instant
32+
import java.time.LocalDateTime
33+
import java.time.ZoneOffset
34+
import java.time.format.DateTimeFormatter
3135
import java.util.*
3236
import javax.annotation.PostConstruct
3337
import javax.annotation.PreDestroy
@@ -160,7 +164,14 @@ class SMTLibZ3DbConstraintSolver() : DbConstraintSolver {
160164
}
161165
is LongValue -> {
162166
gene = if (hasColumnType(schemaDto, table, columnName, "TIMESTAMP")) {
163-
LongGene(columnName, columnValue.value.toLong())
167+
val epochSeconds = columnValue.value.toLong()
168+
val localDateTime = LocalDateTime.ofInstant(
169+
Instant.ofEpochSecond(epochSeconds), ZoneOffset.UTC
170+
)
171+
val formatted = localDateTime.format(
172+
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
173+
)
174+
ImmutableDataHolderGene(columnName, formatted, inQuotes = true)
164175
} else {
165176
IntegerGene(columnName, columnValue.value.toInt())
166177
}

core/src/main/kotlin/org/evomaster/core/solver/SmtLibGenerator.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
523523
// Maps database column types to SMT-LIB types
524524
private val TYPE_MAP = mapOf(
525525
"BIGINT" to "Int",
526+
"BIT" to "Int",
526527
"INTEGER" to "Int",
527528
"TINYINT" to "Int",
529+
"SMALLINT" to "Int",
528530
"TIMESTAMP" to "Int",
529531
"DATE" to "Int",
530532
"FLOAT" to "Real",
@@ -534,8 +536,9 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
534536
"CHARACTER VARYING" to "String",
535537
"CHAR" to "String",
536538
"VARCHAR" to "String",
539+
"TEXT" to "String",
537540
"CHARACTER LARGE OBJECT" to "String",
538-
"BOOLEAN" to "String", // TODO: Check this
541+
"BOOLEAN" to "String",
539542
)
540543
}
541544
}

core/src/test/kotlin/org/evomaster/core/search/gene/ArrayGeneTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package org.evomaster.core.search.gene
22

33
import org.evomaster.core.search.gene.collection.ArrayGene
44
import org.evomaster.core.search.gene.collection.EnumGene
5+
import org.evomaster.core.search.gene.numeric.BigIntegerGene
56
import org.evomaster.core.search.gene.numeric.IntegerGene
67
import org.evomaster.core.search.gene.numeric.LongGene
78
import org.evomaster.core.search.gene.string.StringGene
89
import org.evomaster.core.search.gene.utils.GeneUtils
10+
import org.evomaster.core.search.gene.wrapper.ChoiceGene
911
import org.evomaster.core.search.service.Randomness
1012
import org.junit.jupiter.api.Assertions.assertEquals
13+
import org.junit.jupiter.api.Assertions.assertFalse
1114
import org.junit.jupiter.api.Assertions.assertTrue
1215
import org.junit.jupiter.api.Test
1316
import org.junit.jupiter.api.assertThrows
@@ -207,4 +210,31 @@ class ArrayGeneTest {
207210
assertTrue(!xmlOutput.contains("]"), "XML should not contain square brackets")
208211
assertTrue(!xmlOutput.contains(", "), "XML must not contain commas with spaces")
209212
}
213+
214+
@Test
215+
fun testDoesExistWithDifferentNumericGeneTypes() {
216+
val bigIntegerGeneValue = java.math.BigInteger.valueOf(42L)
217+
val bigIntegerGene = BigIntegerGene("bigInt", bigIntegerGeneValue)
218+
219+
val integerGeneValue = 42
220+
val integerGene = IntegerGene("int", integerGeneValue)
221+
222+
val choiceGene = ChoiceGene("choice", listOf(bigIntegerGene, integerGene))
223+
224+
val arrayWithChoiceGenes = ArrayGene(
225+
"arrayOfChoiceGenes",
226+
template = ChoiceGene("template", listOf(bigIntegerGene, integerGene)),
227+
elements = mutableListOf(choiceGene)
228+
)
229+
230+
val anotherChoiceGene = ChoiceGene("choice", listOf(bigIntegerGene, integerGene))
231+
232+
choiceGene.selectActiveGene(0)
233+
anotherChoiceGene.selectActiveGene(1)
234+
235+
assertFalse(arrayWithChoiceGenes.doesExist(anotherChoiceGene))
236+
237+
}
238+
239+
210240
}

core/src/test/kotlin/org/evomaster/core/solver/SMTLibZ3DbConstraintSolverTest.kt

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package org.evomaster.core.solver
33
import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto
44
import org.evomaster.client.java.sql.DbInfoExtractor
55
import org.evomaster.client.java.sql.SqlScriptRunner
6+
import org.evomaster.core.search.gene.BooleanGene
67
import org.evomaster.core.search.gene.Gene
78
import org.evomaster.core.search.gene.numeric.IntegerGene
9+
import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene
810
import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene
911
import org.evomaster.core.search.gene.string.StringGene
12+
import org.evomaster.core.sql.SqlActionTransformer
1013
import org.junit.jupiter.api.AfterAll
1114
import org.junit.jupiter.api.Assertions.*
1215
import org.junit.jupiter.api.BeforeAll
@@ -27,7 +30,7 @@ class SMTLibZ3DbConstraintSolverTest {
2730
@BeforeAll
2831
fun setup() {
2932
connection = DriverManager.getConnection("jdbc:h2:mem:constraint_test", "sa", "")
30-
SqlScriptRunner.execCommand(connection, "CREATE TABLE users(id bigint generated by default as identity primary key, name varchar(255), age int, points int);\n")
33+
SqlScriptRunner.execCommand(connection, "CREATE TABLE users(id bigint generated by default as identity primary key, name varchar(255), age int, points boolean, flag bit, created_at timestamp not null);\n")
3134
schemaDto = DbInfoExtractor.extract(connection)
3235
solver = SMTLibZ3DbConstraintSolver()
3336
solver.initializeExecutor()
@@ -50,32 +53,44 @@ class SMTLibZ3DbConstraintSolverTest {
5053
@Test
5154
fun selectFromUsers() {
5255

53-
val newActions = solver.solve(schemaDto, "SELECT * FROM Users;", 2)
56+
val newActions = solver.solve(schemaDto, "SELECT * FROM Users WHERE name = 'agus';", 2)
5457

5558
assertEquals(2, newActions.size)
5659

5760
val genesInsert1: List<Gene> = newActions[0].seeTopGenes()
5861

59-
assertEquals(4, genesInsert1.size)
62+
assertEquals(6, genesInsert1.size)
6063

6164
for (gene in genesInsert1) {
6265
when (gene.name) {
6366
"ID" -> {
6467
assertTrue(gene is SqlPrimaryKeyGene)
6568
val child = gene.getViewOfChildren().first()
66-
assertEquals(0, (child as IntegerGene).value)
69+
assertEquals(4, (child as IntegerGene).value)
6770
}
6871
"NAME" -> {
6972
assertTrue(gene is StringGene)
70-
assertEquals("", (gene as StringGene).value)
73+
assertEquals("agus", (gene as StringGene).value)
7174
}
7275
"AGE" -> {
7376
assertTrue(gene is IntegerGene)
74-
assertEquals(0, (gene as IntegerGene).value)
77+
assertEquals(5, (gene as IntegerGene).value)
7578
}
7679
"POINTS" -> {
77-
assertTrue(gene is IntegerGene)
78-
assertEquals(0, (gene as IntegerGene).value)
80+
assertTrue(gene is BooleanGene)
81+
assertEquals(true, (gene as BooleanGene).value)
82+
}
83+
"FLAG" -> {
84+
// H2 maps BIT to BOOLEAN, so the solver produces a BooleanGene
85+
assertTrue(gene is BooleanGene)
86+
assertEquals(true, (gene as BooleanGene).value)
87+
}
88+
"CREATED_AT" -> {
89+
// TIMESTAMP epoch-seconds from Z3 are converted to a formatted datetime string
90+
assertTrue(gene is ImmutableDataHolderGene)
91+
val value = (gene as ImmutableDataHolderGene).value
92+
assertTrue(value.matches(Regex("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")),
93+
"Expected datetime format 'yyyy-MM-dd HH:mm:ss' but got: $value")
7994
}
8095
else -> {
8196
fail("Unexpected gene: $gene")
@@ -85,26 +100,38 @@ class SMTLibZ3DbConstraintSolverTest {
85100

86101
val genesInsert2: List<Gene> = newActions[1].seeTopGenes()
87102

88-
assertEquals(4, genesInsert2.size)
103+
assertEquals(6, genesInsert2.size)
89104

90105
for (gene in genesInsert2) {
91106
when (gene.name) {
92107
"ID" -> {
93108
assertTrue(gene is SqlPrimaryKeyGene)
94109
val child = gene.getViewOfChildren().first()
95-
assertEquals(1, (child as IntegerGene).value)
110+
assertEquals(2, (child as IntegerGene).value)
96111
}
97112
"NAME" -> {
98113
assertTrue(gene is StringGene)
99-
assertEquals("", (gene as StringGene).value)
114+
assertEquals("agus", (gene as StringGene).value)
100115
}
101116
"AGE" -> {
102117
assertTrue(gene is IntegerGene)
103-
assertEquals(0, (gene as IntegerGene).value)
118+
assertEquals(3, (gene as IntegerGene).value)
104119
}
105120
"POINTS" -> {
106-
assertTrue(gene is IntegerGene)
107-
assertEquals(0, (gene as IntegerGene).value)
121+
assertTrue(gene is BooleanGene)
122+
assertEquals(true, (gene as BooleanGene).value)
123+
}
124+
"FLAG" -> {
125+
// H2 maps BIT to BOOLEAN, so the solver produces a BooleanGene
126+
assertTrue(gene is BooleanGene)
127+
assertEquals(true, (gene as BooleanGene).value)
128+
}
129+
"CREATED_AT" -> {
130+
// TIMESTAMP epoch-seconds from Z3 are converted to a formatted datetime string
131+
assertTrue(gene is ImmutableDataHolderGene)
132+
val value = (gene as ImmutableDataHolderGene).value
133+
assertTrue(value.matches(Regex("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")),
134+
"Expected datetime format 'yyyy-MM-dd HH:mm:ss' but got: $value")
108135
}
109136
else -> {
110137
fail("Unexpected gene: $gene")
@@ -113,6 +140,25 @@ class SMTLibZ3DbConstraintSolverTest {
113140
}
114141
}
115142

143+
/**
144+
* End-to-end test: verifies that the rows generated by the solver can be inserted
145+
* into the real database and that the original SELECT query then returns results.
146+
*/
147+
@Test
148+
@Throws(SQLException::class)
149+
fun insertedRowsSatisfySelectQuery() {
150+
val selectQuery = "SELECT * FROM users WHERE age > 25"
151+
152+
val actions = solver.solve(schemaDto, selectQuery, 2)
153+
assertFalse(actions.isEmpty(), "Solver should return actions for a satisfiable query")
154+
155+
val dto = SqlActionTransformer.transform(actions)
156+
SqlScriptRunner.execInsert(connection, dto.insertions)
157+
158+
val result = SqlScriptRunner.execCommand(connection, selectQuery)
159+
assertFalse(result.isEmpty(), "Inserted rows should satisfy the original SELECT query")
160+
}
161+
116162
/**
117163
* Test the generation of SMT from a complicated cache local temporary table does not throw exception
118164
*/

0 commit comments

Comments
 (0)