Skip to content

Commit 02fe06a

Browse files
committed
Make symbolic execs preferable in minimization process
1 parent 6860479 commit 02fe06a

File tree

3 files changed

+50
-11
lines changed

3 files changed

+50
-11
lines changed

utbot-framework-test/src/test/kotlin/org/utbot/framework/minimization/MinimizationGreedyEssentialTest.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,18 @@ class MinimizationGreedyEssentialTest {
6868
val minimizedExecutions = GreedyEssential.minimize(executions)
6969
assertEquals((1..size).toList(), minimizedExecutions.sorted())
7070
}
71+
72+
@Test
73+
fun testExecutionPriority() {
74+
val executions = mapOf(
75+
0 to listOf(0, 1, 2, 3, 4),
76+
1 to listOf(0, 1, 2, 3, 4)
77+
)
78+
val executionToPriority = mapOf(
79+
0 to 1,
80+
1 to 0
81+
)
82+
val minimizedExecutions = GreedyEssential.minimize(executions, executionToPriority)
83+
assertEquals(listOf(1), minimizedExecutions)
84+
}
7185
}

utbot-framework/src/main/kotlin/org/utbot/framework/minimization/GreedyEssential.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ private inline class LineNumber(val number: Int)
1010
* [Greedy essential algorithm](CONFLUENCE:Test+Minimization)
1111
*/
1212
class GreedyEssential private constructor(
13-
executionToCoveredLines: Map<ExecutionNumber, List<LineNumber>>
13+
executionToCoveredLines: Map<ExecutionNumber, List<LineNumber>>,
14+
executionToPriority: Map<ExecutionNumber, Int>
1415
) {
1516
private val executionToUsefulLines: Map<ExecutionNumber, MutableSet<LineNumber>> =
1617
executionToCoveredLines
@@ -23,8 +24,11 @@ class GreedyEssential private constructor(
2324
.mapValues { it.value.toMutableSet() }
2425

2526
private val executionByPriority =
26-
PriorityQueue(compareByDescending<Pair<ExecutionNumber, Int>> { it.second }.thenBy { it.first.number })
27-
.apply {
27+
PriorityQueue(
28+
compareBy<Pair<ExecutionNumber, Int>> { executionToPriority[it.first] }
29+
.thenByDescending { it.second }
30+
.thenBy { it.first.number }
31+
).apply {
2832
addAll(
2933
executionToCoveredLines
3034
.keys
@@ -89,12 +93,16 @@ class GreedyEssential private constructor(
8993
*
9094
* @return retained execution ids.
9195
*/
92-
fun minimize(executions: Map<Int, List<Int>>): List<Int> {
96+
fun minimize(executions: Map<Int, List<Int>>, executionToPriority: Map<Int, Int> = mapOf()): List<Int> {
9397
val convertedExecutions = executions
9498
.entries
9599
.associate { (execution, lines) -> ExecutionNumber(execution) to lines.map { LineNumber(it) } }
96100

97-
val prioritizer = GreedyEssential(convertedExecutions)
101+
val convertedExecutionToPriority = executionToPriority
102+
.entries
103+
.associate { (execution, priority) -> ExecutionNumber(execution) to priority }
104+
105+
val prioritizer = GreedyEssential(convertedExecutions, convertedExecutionToPriority)
98106
val list = mutableListOf<ExecutionNumber>()
99107
while (prioritizer.hasMore()) {
100108
list.add(prioritizer.getExecutionAndRemove())

utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.utbot.framework.plugin.api.UtModel
1818
import org.utbot.framework.plugin.api.UtNullModel
1919
import org.utbot.framework.plugin.api.UtPrimitiveModel
2020
import org.utbot.framework.plugin.api.UtStatementModel
21+
import org.utbot.framework.plugin.api.UtSymbolicExecution
2122
import org.utbot.framework.plugin.api.UtVoidModel
2223

2324

@@ -53,8 +54,11 @@ fun minimizeExecutions(executions: List<UtExecution>): List<UtExecution> {
5354
executions.indices.filter { executions[it].coverage?.coveredInstructions?.isEmpty() ?: true }.toSet()
5455
// ^^^ here we add executions with empty or null coverage, because it happens only if a concrete execution failed,
5556
// so we don't know the actual coverage for such executions
56-
val mapping = buildMapping(executions.filterIndexed { idx, _ -> idx !in unknownCoverageExecutions })
57-
val usedExecutionIndexes = (GreedyEssential.minimize(mapping) + unknownCoverageExecutions).toSet()
57+
58+
val filteredExecutions = executions.filterIndexed { idx, _ -> idx !in unknownCoverageExecutions }
59+
val (mapping, executionToPriorityMapping) = buildMapping(filteredExecutions)
60+
61+
val usedExecutionIndexes = (GreedyEssential.minimize(mapping, executionToPriorityMapping) + unknownCoverageExecutions).toSet()
5862

5963
val usedMinimizedExecutions = executions.filterIndexed { idx, _ -> idx in usedExecutionIndexes }
6064

@@ -143,13 +147,14 @@ private fun <T : Any> groupExecutionsByTestSuite(
143147
executions.groupBy { executionToTestSuite(it) }.values
144148

145149
/**
146-
* Builds a mapping from execution id to edges id.
150+
* Builds a mapping from execution id to edges id and from execution id to its priority.
147151
*/
148-
private fun buildMapping(executions: List<UtExecution>): Map<Int, List<Int>> {
152+
private fun buildMapping(executions: List<UtExecution>): Pair<Map<Int, List<Int>>, Map<Int, Int>> {
149153
// (inst1, instr2) -> edge id --- edge represents as a pair of instructions, which are connected by this edge
150154
val allCoveredEdges = mutableMapOf<Pair<Long, Long>, Int>()
151155
val thrownExceptions = mutableMapOf<String, Long>()
152156
val mapping = mutableMapOf<Int, List<Int>>()
157+
val executionToPriorityMapping = mutableMapOf<Int, Int>()
153158

154159

155160
executions.forEachIndexed { idx, execution ->
@@ -169,11 +174,12 @@ private fun buildMapping(executions: List<UtExecution>): Map<Int, List<Int>> {
169174
edges += allCoveredEdges[instructions[i] to instructions[i + 1]]!!
170175
}
171176
mapping[idx] = edges
177+
executionToPriorityMapping[idx] = execution.getExecutionPriority()
172178
}
173179
}
174180
}
175181

176-
return mapping
182+
return Pair(mapping, executionToPriorityMapping)
177183
}
178184

179185
/**
@@ -264,4 +270,15 @@ private fun addExtraIfLastInstructionIsException(
264270
* Takes an exception name, a class name, a method signature and a line number from exception.
265271
*/
266272
private fun Throwable.exceptionToInfo(): String =
267-
this::class.java.name + (this.stackTrace.firstOrNull()?.run { className + methodName + lineNumber } ?: "null")
273+
this::class.java.name + (this.stackTrace.firstOrNull()?.run { className + methodName + lineNumber } ?: "null")
274+
275+
/**
276+
* Returns an execution priority. [UtSymbolicExecution] has the highest priority
277+
* over other executions like [UtFuzzedExecution], [UtFailedExecution], etc.
278+
*
279+
* See [https://github.com/UnitTestBot/UTBotJava/issues/1504] for more details.
280+
*/
281+
private fun UtExecution.getExecutionPriority(): Int = when (this) {
282+
is UtSymbolicExecution -> 0
283+
else -> 1
284+
}

0 commit comments

Comments
 (0)