Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,14 @@ class FitnessValue(

aggregatedFailedWhereQueries.clear()
aggregatedFailedWhereQueries.addAll(
databaseExecutions.values.flatMap { a -> a.executionInfo }.map{ b -> b.sqlCommand }
databaseExecutions.values
// Only the commands whose WHERE clause actually failed are relevant.
.filter { it.failedWhere.isNotEmpty() }
.flatMap { it.executionInfo }
.map { it.sqlCommand }
// JSQLParser >= 4.9 may produce empty-string SQL commands (it used to throw, now
// parses "" to null). Guard here at the source rather than at every call site.
.filter { it.isNotBlank() }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this definition of aggregatedFailedWhereQueries correct? i mean, also the previous version. inside DatabaseExecution we keep track of all SQL commands done in a test. some have failedWhere, some do not. but here it seems like you mark everything under executionInfo as failedWhere, isn't it? this would work if and only if all queries had failedWhere, isn't it?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to be on safe side, can you add some tests to check this scenario?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be covered by line 169 .filter { it.failedWhere.isNotEmpty() }?

I added a test also to check both conditions failed where present and absent

)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.evomaster.core.search

import org.evomaster.client.java.sql.DbInfoExtractor
import org.evomaster.client.java.sql.SqlScriptRunner
import org.evomaster.client.java.sql.internal.SqlHandler
import org.evomaster.client.java.controller.api.dto.database.execution.SqlExecutionLogDto
import org.evomaster.core.sql.DatabaseExecution
import org.evomaster.core.sql.schema.TableId
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
import java.sql.Connection
import java.sql.DriverManager

class FitnessValueSqlIntegrationTest {

companion object {

private lateinit var connection: Connection

@BeforeAll
@JvmStatic
fun initClass() {
connection = DriverManager.getConnection("jdbc:h2:mem:db_test", "sa", "")
}

@AfterAll
@JvmStatic
fun clean() {
connection.close()
}
}

@BeforeEach
fun initTest() {
SqlScriptRunner.execCommand(connection, "DROP ALL OBJECTS;")
SqlScriptRunner.execCommand(connection, """
CREATE TABLE Person (
person_id INT PRIMARY KEY,
age INT
);
""")
}

private fun runSelect(sqlCommand: String): DatabaseExecution {
val schema = DbInfoExtractor.extract(connection)
val tableIds = schema.tables.map { t -> TableId(t.id.name) }.toSet()

val handler = SqlHandler(null)
handler.setConnection(connection)
handler.setSchema(schema)

handler.handle(SqlExecutionLogDto(sqlCommand, false, 0L))
handler.getSqlDistances(null, true)

return DatabaseExecution.fromDto(handler.getExecutionDto(), tableIds)
}

@Test
fun testSelectWithFailedWhereIsCollected() {
// Empty table: WHERE clause will fail (no rows match)
val sqlCommand = "SELECT * FROM Person WHERE age = 30"

val fv = FitnessValue(1.0)
fv.setDatabaseExecution(0, runSelect(sqlCommand))
fv.aggregateDatabaseData()

val queries = fv.getViewOfAggregatedFailedWhereQueries()
assertEquals(1, queries.size)
assertTrue(queries.contains(sqlCommand))
}

@Test
fun testSelectWithSuccessfulWhereIsNotCollected() {
// Insert a row so the WHERE clause succeeds
SqlScriptRunner.execCommand(connection, "INSERT INTO Person VALUES (1, 30)")
val sqlCommand = "SELECT * FROM Person WHERE age = 30"

val fv = FitnessValue(1.0)
fv.setDatabaseExecution(0, runSelect(sqlCommand))
fv.aggregateDatabaseData()

val queries = fv.getViewOfAggregatedFailedWhereQueries()
assertTrue(queries.isEmpty())
}
}
77 changes: 77 additions & 0 deletions core/src/test/kotlin/org/evomaster/core/search/FitnessValueTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import org.evomaster.client.java.controller.api.dto.BootTimeInfoDto
import org.evomaster.client.java.controller.api.dto.TargetInfoDto
import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming
import org.evomaster.core.search.service.IdMapper
import org.evomaster.core.sql.DatabaseExecution
import org.evomaster.core.sql.SqlExecutionInfo
import org.evomaster.core.sql.schema.TableId
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test

Expand Down Expand Up @@ -123,4 +126,78 @@ class FitnessValueTest {
assertEquals(3, linesInfo.searchTime)
}

@Test
fun testAggregatedFailedWhereQueriesOnlyFromExecutionsWithFailedWhere() {
val fv = FitnessValue(1.0)

val tableId = TableId("foo")

// Execution that has a failing WHERE clause — its queries should be collected
val executionWithFailedWhere = DatabaseExecution(
queriedData = emptyMap(),
updatedData = emptyMap(),
insertedData = emptyMap(),
failedWhere = mapOf(tableId to setOf("id")),
deletedData = emptyList(),
numberOfSqlCommands = 1,
sqlParseFailureCount = 0,
executionInfo = listOf(
SqlExecutionInfo("SELECT * FROM foo WHERE id = 1", false, 10L)
)
)

// Execution with no failing WHERE clause — its queries must NOT be collected,
// even though it has executionInfo entries
val executionWithoutFailedWhere = DatabaseExecution(
queriedData = emptyMap(),
updatedData = emptyMap(),
insertedData = emptyMap(),
failedWhere = emptyMap(),
deletedData = emptyList(),
numberOfSqlCommands = 1,
sqlParseFailureCount = 0,
executionInfo = listOf(
SqlExecutionInfo("INSERT INTO bar VALUES (1)", false, 5L)
)
)

fv.setDatabaseExecution(0, executionWithFailedWhere)
fv.setDatabaseExecution(1, executionWithoutFailedWhere)
fv.aggregateDatabaseData()

val queries = fv.getViewOfAggregatedFailedWhereQueries()
assertEquals(1, queries.size)
assertTrue(queries.contains("SELECT * FROM foo WHERE id = 1"))
}

@Test
fun testAggregatedFailedWhereQueriesExcludesBlankSqlCommands() {
val fv = FitnessValue(1.0)

val tableId = TableId("foo")

// JSQLParser >= 4.9 can produce empty-string SQL commands; they must be filtered out
// at the source in aggregateDatabaseData() rather than at every call site
val execution = DatabaseExecution(
queriedData = emptyMap(),
updatedData = emptyMap(),
insertedData = emptyMap(),
failedWhere = mapOf(tableId to setOf("id")),
deletedData = emptyList(),
numberOfSqlCommands = 3,
sqlParseFailureCount = 0,
executionInfo = listOf(
SqlExecutionInfo("SELECT * FROM foo WHERE id = 1", false, 10L),
SqlExecutionInfo("", false, 5L),
SqlExecutionInfo(" ", false, 5L)
)
)
fv.setDatabaseExecution(0, execution)
fv.aggregateDatabaseData()

val queries = fv.getViewOfAggregatedFailedWhereQueries()
assertEquals(1, queries.size)
assertTrue(queries.contains("SELECT * FROM foo WHERE id = 1"))
}

}