Skip to content

Commit b7ea573

Browse files
authored
Add an option to control fuzzing #451 (#552)
1 parent 74e7a34 commit b7ea573

File tree

10 files changed

+200
-199
lines changed

10 files changed

+200
-199
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ object UtSettings {
270270
/**
271271
* Fuzzer tries to generate and run tests during this time.
272272
*/
273-
var fuzzingTimeoutInMillis: Int by getIntProperty(3_000)
273+
var fuzzingTimeoutInMillis: Long by getLongProperty(3_000L)
274274

275275
/**
276276
* Generate tests that treat possible overflows in arithmetic operations as errors

utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import kotlinx.coroutines.GlobalScope
55
import kotlinx.coroutines.cancel
66
import kotlinx.coroutines.flow.Flow
77
import kotlinx.coroutines.flow.collect
8-
import kotlinx.coroutines.flow.flattenConcat
9-
import kotlinx.coroutines.flow.flowOf
108
import kotlinx.coroutines.isActive
119
import kotlinx.coroutines.launch
1210
import kotlinx.coroutines.runBlocking
@@ -119,15 +117,15 @@ open class TestCaseGenerator(
119117
executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1)
120118
): Flow<UtResult> {
121119
val engine = createSymbolicEngine(controller, method, mockStrategy, chosenClassesToMockAlways, executionTimeEstimator)
122-
return createDefaultFlow(engine)
120+
return defaultTestFlow(engine, executionTimeEstimator.userTimeout)
123121
}
124122

125123
fun generate(
126124
methods: List<UtMethod<*>>,
127125
mockStrategy: MockStrategyApi,
128126
chosenClassesToMockAlways: Set<ClassId> = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id },
129127
methodsGenerationTimeout: Long = utBotGenerationTimeoutInMillis,
130-
generate: (engine: UtBotSymbolicEngine) -> Flow<UtResult> = ::createDefaultFlow
128+
generate: (engine: UtBotSymbolicEngine) -> Flow<UtResult> = defaultTestFlow(methodsGenerationTimeout)
131129
): List<UtMethodTestSet> {
132130
if (isCanceled()) return methods.map { UtMethodTestSet(it) }
133131

@@ -260,17 +258,6 @@ open class TestCaseGenerator(
260258
)
261259
}
262260

263-
private fun createDefaultFlow(engine: UtBotSymbolicEngine): Flow<UtResult> {
264-
var flow = engine.traverse()
265-
if (UtSettings.useFuzzing) {
266-
flow = flowOf(
267-
engine.fuzzing(System.currentTimeMillis() + UtSettings.fuzzingTimeoutInMillis),
268-
flow,
269-
).flattenConcat()
270-
}
271-
return flow
272-
}
273-
274261
// CONFLUENCE:The+UtBot+Java+timeouts
275262

276263
class ExecutionTimeEstimator(val userTimeout: Long, methodsUnderTestNumber: Int) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.utbot.framework.plugin.api
2+
3+
import kotlinx.coroutines.flow.Flow
4+
import kotlinx.coroutines.flow.emptyFlow
5+
import kotlinx.coroutines.flow.flattenConcat
6+
import kotlinx.coroutines.flow.flowOf
7+
import org.utbot.engine.UtBotSymbolicEngine
8+
import org.utbot.framework.UtSettings
9+
10+
/**
11+
* Constructs [TestFlow] for customization and creates flow producer.
12+
*/
13+
fun testFlow(block: TestFlow.() -> Unit): UtBotSymbolicEngine.() -> Flow<UtResult> = { TestFlow(block).build(this) }
14+
15+
/**
16+
* Creates default flow that uses [UtSettings] for customization.
17+
*/
18+
fun defaultTestFlow(timeout: Long) = testFlow {
19+
isSymbolicEngineEnabled = true
20+
generationTimeout = timeout
21+
isFuzzingEnabled = UtSettings.useFuzzing
22+
if (generationTimeout > 0) {
23+
fuzzingValue = UtSettings.fuzzingTimeoutInMillis.coerceIn(0, generationTimeout) / generationTimeout.toDouble()
24+
}
25+
}
26+
27+
/**
28+
* Creates default flow that uses [UtSettings] for customization.
29+
*/
30+
fun defaultTestFlow(engine: UtBotSymbolicEngine, timeout: Long) = defaultTestFlow(timeout).invoke(engine)
31+
32+
/**
33+
* TestFlow helps construct flows with different settings but with common sequence of test generation process.
34+
*
35+
* Use [testFlow] to set a custom test flow or [defaultTestFlow] to create flow based on [UtSettings].
36+
*/
37+
class TestFlow internal constructor(block: TestFlow.() -> Unit) {
38+
var generationTimeout = 0L
39+
set(value) {
40+
field = maxOf(0, value)
41+
}
42+
var isSymbolicEngineEnabled = true
43+
var isFuzzingEnabled = false
44+
var fuzzingValue: Double = 0.1
45+
set(value) {
46+
field = value.coerceIn(0.0, 1.0)
47+
}
48+
49+
init {
50+
apply(block)
51+
}
52+
53+
/*
54+
Constructs default flow that is having the following steps at the moment:
55+
1. If fuzzer is enabled then run it before symbolic execution for [fuzzingValue] * [generationTimeout] ms.
56+
2. Run symbolic execution for the rest time.
57+
3. If both (fuzzer and symbolic execution) are off then do nothing.
58+
*/
59+
fun build(engine: UtBotSymbolicEngine): Flow<UtResult> {
60+
return when {
61+
generationTimeout == 0L -> emptyFlow()
62+
isFuzzingEnabled -> {
63+
when (val value = if (isSymbolicEngineEnabled) (fuzzingValue * generationTimeout).toLong() else generationTimeout) {
64+
0L -> engine.traverse()
65+
generationTimeout -> engine.fuzzing()
66+
else -> flowOf(
67+
engine.fuzzing(System.currentTimeMillis() + value),
68+
engine.traverse()
69+
).flattenConcat()
70+
}
71+
}
72+
isSymbolicEngineEnabled -> engine.traverse()
73+
else -> emptyFlow()
74+
}
75+
}
76+
}

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.openapi.application.invokeLater
88
import com.intellij.openapi.compiler.CompileContext
99
import com.intellij.openapi.compiler.CompilerManager
1010
import com.intellij.openapi.compiler.CompilerPaths
11+
import com.intellij.openapi.components.service
1112
import com.intellij.openapi.module.Module
1213
import com.intellij.openapi.progress.ProgressIndicator
1314
import com.intellij.openapi.progress.Task
@@ -49,6 +50,8 @@ import java.nio.file.Paths
4950
import java.util.concurrent.TimeUnit
5051
import org.utbot.common.filterWhen
5152
import org.utbot.engine.util.mockListeners.ForceStaticMockListener
53+
import org.utbot.framework.plugin.api.testFlow
54+
import org.utbot.intellij.plugin.settings.Settings
5255
import kotlin.reflect.KClass
5356
import kotlin.reflect.full.functions
5457

@@ -188,7 +191,18 @@ object UtTestsDialogProcessor {
188191

189192
val notEmptyCases = withUtContext(context) {
190193
testCaseGenerator
191-
.generate(methods, model.mockStrategy, model.chosenClassesToMockAlways, model.timeout)
194+
.generate(
195+
methods,
196+
model.mockStrategy,
197+
model.chosenClassesToMockAlways,
198+
model.timeout,
199+
generate = testFlow {
200+
generationTimeout = model.timeout
201+
isSymbolicEngineEnabled = true
202+
isFuzzingEnabled = UtSettings.useFuzzing
203+
fuzzingValue = project.service<Settings>().fuzzingValue
204+
}
205+
)
192206
.map { it.summarize(searchDirectory) }
193207
.filterNot { it.executions.isEmpty() && it.errors.isEmpty() }
194208
}

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ data class GenerateTestsModel(
2828
var selectedMethods: Set<MemberInfo>?,
2929
var timeout:Long,
3030
var generateWarningsForStaticMocking: Boolean = false,
31+
var fuzzingValue: Double = 0.05
3132
) {
3233
var testSourceRoot: VirtualFile? = null
3334
var testPackageName: String? = null

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Configurable.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Configurable(val project: Project) : SearchableConfigurable {
1111

1212
override fun createComponent(): JComponent = settingsWindow.panel
1313

14-
override fun isModified(): Boolean = settingsWindow.isModified
14+
override fun isModified(): Boolean = settingsWindow.isModified()
1515

1616
override fun apply() = settingsWindow.apply()
1717

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class Settings(val project: Project) : PersistentStateComponent<Settings.State>
5151
var forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem,
5252
var treatOverflowAsError: TreatOverflowAsError = TreatOverflowAsError.defaultItem,
5353
var parametrizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem,
54-
var classesToMockAlways: Array<String> = Mocker.defaultSuperClassesToMockAlwaysNames.toTypedArray()
54+
var classesToMockAlways: Array<String> = Mocker.defaultSuperClassesToMockAlwaysNames.toTypedArray(),
55+
var fuzzingValue: Double = 0.05,
5556
) {
5657
constructor(model: GenerateTestsModel) : this(
5758
codegenLanguage = model.codegenLanguage,
@@ -63,7 +64,8 @@ class Settings(val project: Project) : PersistentStateComponent<Settings.State>
6364
hangingTestsTimeout = model.hangingTestsTimeout,
6465
forceStaticMocking = model.forceStaticMocking,
6566
parametrizedTestSource = model.parametrizedTestSource,
66-
classesToMockAlways = model.chosenClassesToMockAlways.mapTo(mutableSetOf()) { it.name }.toTypedArray()
67+
classesToMockAlways = model.chosenClassesToMockAlways.mapTo(mutableSetOf()) { it.name }.toTypedArray(),
68+
fuzzingValue = model.fuzzingValue
6769
)
6870

6971
override fun equals(other: Any?): Boolean {
@@ -83,6 +85,7 @@ class Settings(val project: Project) : PersistentStateComponent<Settings.State>
8385
if (treatOverflowAsError != other.treatOverflowAsError) return false
8486
if (parametrizedTestSource != other.parametrizedTestSource) return false
8587
if (!classesToMockAlways.contentEquals(other.classesToMockAlways)) return false
88+
if (fuzzingValue != other.fuzzingValue) return false
8689

8790
return true
8891
}
@@ -98,6 +101,7 @@ class Settings(val project: Project) : PersistentStateComponent<Settings.State>
98101
result = 31 * result + treatOverflowAsError.hashCode()
99102
result = 31 * result + parametrizedTestSource.hashCode()
100103
result = 31 * result + classesToMockAlways.contentHashCode()
104+
result = 31 * result + fuzzingValue.hashCode()
101105

102106
return result
103107
}
@@ -129,6 +133,12 @@ class Settings(val project: Project) : PersistentStateComponent<Settings.State>
129133

130134
val classesToMockAlways: Set<String> get() = state.classesToMockAlways.toSet()
131135

136+
var fuzzingValue: Double
137+
get() = state.fuzzingValue
138+
set(value) {
139+
state.fuzzingValue = value.coerceIn(0.0, 1.0)
140+
}
141+
132142
fun setClassesToMockAlways(classesToMockAlways: List<String>) {
133143
state.classesToMockAlways = classesToMockAlways.distinct().toTypedArray()
134144
}
@@ -217,8 +227,7 @@ private class HangingTestsTimeoutConverter : Converter<HangingTestsTimeout>() {
217227

218228
override fun fromString(value: String): HangingTestsTimeout {
219229
val arguments = value.substringAfter("HangingTestsTimeout:")
220-
val timeoutMs = arguments.first().toLong()
221-
230+
val timeoutMs = arguments.toLong()
222231
return HangingTestsTimeout(timeoutMs)
223232
}
224233
}

0 commit comments

Comments
 (0)