Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Minimize UtExecution number produced by fuzzing and collect coverage …
…statistic
  • Loading branch information
Markoutte committed Jul 8, 2022
commit 3c39682de8ae56ab3527f44881e4c4d9a4db3cb8
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ import org.utbot.framework.plugin.api.util.utContext
import org.utbot.framework.plugin.api.util.description
import org.utbot.fuzzer.FallbackModelProvider
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.Trie
import org.utbot.fuzzer.collectConstantsForFuzzer
import org.utbot.fuzzer.defaultModelProviders
import org.utbot.fuzzer.fuzz
Expand Down Expand Up @@ -408,7 +410,8 @@ class UtBotSymbolicEngine(
parameterNameMap = { index -> names?.getOrNull(index) }
}
val modelProviderWithFallback = modelProvider(defaultModelProviders { nextDefaultModelId++ }).withFallback(fallbackModelProvider::toModel)
val coveredInstructionTracker = mutableSetOf<Instruction>()
val coveredInstructionTracker = Trie(Instruction::id)
val coveredInstructorValues = mutableMapOf<Trie.Node<Instruction>, List<FuzzedValue>>()
var attempts = UtSettings.fuzzingMaxAttempts
fuzz(methodUnderTestDescription, modelProviderWithFallback).forEach { values ->
if (System.currentTimeMillis() >= until) {
Expand All @@ -431,12 +434,14 @@ class UtBotSymbolicEngine(
}
}

if (!coveredInstructionTracker.addAll(concreteExecutionResult.coverage.coveredInstructions)) {
val count = coveredInstructionTracker.add(concreteExecutionResult.coverage.coveredInstructions)
if (count.count > 1) {
if (--attempts < 0) {
return@flow
}
return@forEach
}

coveredInstructorValues[count] = values
val nameSuggester = sequenceOf(ModelBasedNameSuggester(), MethodBasedNameSuggester())
val testMethodName = try {
nameSuggester.flatMap { it.suggest(methodUnderTestDescription, values, concreteExecutionResult.result) }.firstOrNull()
Expand Down
130 changes: 130 additions & 0 deletions utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Trie.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.utbot.fuzzer

fun <T> trieOf(vararg values: Iterable<T>): Trie<T, T> = IdentityTrie<T>().apply {
values.forEach(this::add)
}

fun stringTrieOf(vararg values: String): StringTrie = StringTrie().apply {
values.forEach(this::add)
}

class StringTrie : IdentityTrie<Char>() {
fun add(string: String) = super.add(string.toCharArray().asIterable())
fun remove(string: String) = super.remove(string.toCharArray().asIterable())
operator fun get(string: String) = super.get(string.toCharArray().asIterable())
fun collect() = asSequence().map { String(it.toCharArray()) }.toSet()
}

open class IdentityTrie<T> : Trie<T, T>({it})
Comment thread
sergeypospelov marked this conversation as resolved.
Outdated

open class Trie<T, K>(
private val keyExtractor: (T) -> K
) : Iterable<List<T>> {

private val roots = HashMap<K, NodeImpl<T, K>>()
private val implementations = HashMap<Node<T>, NodeImpl<T, K>>()

fun add(values: Iterable<T>): Node<T> {
val root = try { values.first() } catch (e: NoSuchElementException) { error("Empty list are not allowed") }
Comment thread
sergeypospelov marked this conversation as resolved.
var key = keyExtractor(root)
var node = roots.computeIfAbsent(key) { NodeImpl(root, null) }
values.asSequence().drop(1).forEach { value ->
key = keyExtractor(value)
node = node.children.computeIfAbsent(key) { NodeImpl(value, node) }
}
node.count++
implementations[node] = node
return node
}

fun remove(values: Iterable<T>): Node<T>? {
val node = findImpl(values) ?: return null
if (node.count > 0 && node.children.isEmpty()) {
var n: NodeImpl<T, K>? = node
while (n != null) {
val key = keyExtractor(n.data)
n = n.parent
if (n == null) {
val removed = roots.remove(key)
check(removed != null)
} else {
val removed = n.children.remove(key)
check(removed != null)
if (n.count != 0) {
break
}
}
}
}
return if (node.count > 0) {
node.count = 0
implementations.remove(node)
node
} else {
null
}
}

operator fun get(values: Iterable<T>): Node<T>? {
return findImpl(values)
}

operator fun get(node: Node<T>): List<T>? {
return implementations[node]?.let(this::buildValue)
}

private fun findImpl(values: Iterable<T>): NodeImpl<T, K>? {
val root = try { values.first() } catch (e: NoSuchElementException) { return null }
Comment thread
sergeypospelov marked this conversation as resolved.
var key = keyExtractor(root)
var node = roots[key] ?: return null
values.asSequence().drop(1).forEach { value ->
key = keyExtractor(value)
node = node.children[key] ?: return null
}
return node.takeIf { it.count > 0 }
}

override fun iterator(): Iterator<List<T>> {
return iterator {
roots.values.forEach { node ->
traverseImpl(node)
}
}
}

private suspend fun SequenceScope<List<T>>.traverseImpl(node: NodeImpl<T, K>) {
val stack = ArrayDeque<NodeImpl<T, K>>()
stack.addLast(node)
while (stack.isNotEmpty()) {
val n = stack.removeLast()
if (n.count > 0) {
yield(buildValue(n))
}
n.children.values.forEach(stack::addLast)
}
}

private fun buildValue(node: NodeImpl<T, K>): List<T> {
return generateSequence(node) { it.parent }.map { it.data }.toList().asReversed()
}

interface Node<T>{
Comment thread
sergeypospelov marked this conversation as resolved.
Outdated
val data: T
val count: Int
}

/**
* Trie node
*
* @param data data to be stored
* @param parent reference to the previous element of the value
* @param count number of value insertions
* @param children list of children mapped by their key
*/
private class NodeImpl<T, K>(
override val data: T,
val parent: NodeImpl<T, K>?,
override var count: Int = 0,
val children: MutableMap<K, NodeImpl<T, K>> = HashMap(),
) : Node<T>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.utbot.framework.plugin.api

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.utbot.fuzzer.Trie
import org.utbot.fuzzer.stringTrieOf
import org.utbot.fuzzer.trieOf

class TrieTest {

@Test
fun simpleTest() {
val trie = stringTrieOf()
assertThrows(java.lang.IllegalStateException::class.java) {
trie.add(emptyList())
}
assertEquals(1, trie.add("Tree").count)
assertEquals(2, trie.add("Tree").count)
assertEquals(1, trie.add("Trees").count)
assertEquals(1, trie.add("Treespss").count)
assertEquals(1, trie.add("Game").count)
assertEquals(1, trie.add("Gamer").count)
assertEquals(1, trie.add("Games").count)
assertEquals(2, trie["Tree"]?.count)
assertEquals(1, trie["Trees"]?.count)
assertEquals(1, trie["Gamer"]?.count)
assertNull(trie["Treesp"])
assertNull(trie["Treessss"])

assertEquals(setOf("Tree", "Trees", "Treespss", "Game", "Gamer", "Games"), trie.collect())
}

@Test
fun testSingleElement() {
val trie = trieOf(listOf(1))
assertEquals(1, trie.toList().size)
}

@Test
fun testRemoval() {
val trie = stringTrieOf()
trie.add("abc")
assertEquals(1, trie.toList().size)
trie.add("abcd")
assertEquals(2, trie.toList().size)
trie.add("abcd")
assertEquals(2, trie.toList().size)
trie.add("abcde")
assertEquals(3, trie.toList().size)

assertNotNull(trie.remove("abcd"))
assertEquals(2, trie.toList().size)

assertNull(trie.remove("ffff"))
assertEquals(2, trie.toList().size)

assertNotNull(trie.remove("abcde"))
assertEquals(1, trie.toList().size)

assertNotNull(trie.remove("abc"))
assertEquals(0, trie.toList().size)
}

@Test
fun testTraverse() {
val trie = Trie(Data::id).apply {
add((1..10).map { Data(it.toLong(), it) })
add((1..10).mapIndexed { index, it -> if (index == 5) Data(3L, it) else Data(it.toLong(), it) })
}

val paths = trie.toList()
assertEquals(2, paths.size)
assertNotEquals(paths[0], paths[1])
}

@Test
fun testNoDuplications() {
val trie = trieOf(
(1..10),
(1..10),
(1..10),
(1..10),
(1..10),
)

assertEquals(1, trie.toList().size)
assertEquals(5, trie[(1..10)]!!.count)
}

@Test
fun testAcceptsNulls() {
val trie = trieOf(
listOf(null),
listOf(null, null),
listOf(null, null, null),
)

assertEquals(3, trie.toList().size)
for (i in 1 .. 3) {
assertEquals(1, trie[(1..i).map { null }]!!.count)
}
}

@Test
fun testAddPrefixAfterWord() {
val trie = stringTrieOf()
trie.add("Hello, world!")
trie.add("Hello")

assertEquals(setOf("Hello, world!", "Hello"), trie.collect())
}

data class Data(val id: Long, val number: Int)
}