@@ -3,47 +3,27 @@ package org.utbot.python
3
3
import kotlinx.coroutines.flow.Flow
4
4
import kotlinx.coroutines.flow.flow
5
5
import mu.KotlinLogging
6
- import org.utbot.framework.plugin.api.DocRegularStmt
7
- import org.utbot.framework.plugin.api.EnvironmentModels
8
- import org.utbot.framework.plugin.api.Instruction
9
- import org.utbot.framework.plugin.api.UtError
10
- import org.utbot.framework.plugin.api.UtExecutionResult
11
- import org.utbot.framework.plugin.api.UtExecutionSuccess
12
- import org.utbot.framework.plugin.api.UtExplicitlyThrownException
13
- import org.utbot.framework.plugin.api.UtModel
14
- import org.utbot.fuzzer.FuzzedValue
6
+ import org.utbot.framework.plugin.api.*
15
7
import org.utbot.fuzzer.UtFuzzedExecution
16
8
import org.utbot.fuzzing.Control
17
9
import org.utbot.fuzzing.fuzz
18
10
import org.utbot.fuzzing.utils.Trie
19
- import org.utbot.python.code.MemoryDump
20
- import org.utbot.python.code.toPythonTree
21
- import org.utbot.python.evaluation.PythonCodeExecutor
22
- import org.utbot.python.evaluation.PythonCodeExecutorImpl
23
- import org.utbot.python.evaluation.PythonEvaluationError
24
- import org.utbot.python.evaluation.PythonEvaluationSuccess
25
- import org.utbot.python.evaluation.PythonEvaluationTimeout
11
+ import org.utbot.python.evaluation.*
12
+ import org.utbot.python.evaluation.serialiation.MemoryDump
13
+ import org.utbot.python.evaluation.serialiation.toPythonTree
26
14
import org.utbot.python.framework.api.python.PythonTreeModel
27
- import org.utbot.python.fuzzing.PythonFeedback
28
- import org.utbot.python.fuzzing.PythonFuzzedConcreteValue
29
- import org.utbot.python.fuzzing.PythonFuzzedValue
30
- import org.utbot.python.fuzzing.PythonFuzzing
31
- import org.utbot.python.fuzzing.PythonMethodDescription
15
+ import org.utbot.python.framework.api.python.PythonTreeWrapper
16
+ import org.utbot.python.fuzzing.*
32
17
import org.utbot.python.newtyping.PythonTypeStorage
33
18
import org.utbot.python.newtyping.general.Type
34
19
import org.utbot.python.newtyping.pythonModules
35
20
import org.utbot.python.newtyping.pythonTypeRepresentation
36
21
import org.utbot.python.utils.camelToSnakeCase
37
22
import org.utbot.summary.fuzzer.names.TestSuggestedInfo
23
+ import java.net.ServerSocket
38
24
39
25
private val logger = KotlinLogging .logger {}
40
26
41
- sealed interface FuzzingExecutionFeedback
42
- class ValidExecution (val utFuzzedExecution : UtFuzzedExecution ): FuzzingExecutionFeedback
43
- class InvalidExecution (val utError : UtError ): FuzzingExecutionFeedback
44
- class TypeErrorFeedback (val message : String ) : FuzzingExecutionFeedback
45
- class ArgumentsTypeErrorFeedback (val message : String ) : FuzzingExecutionFeedback
46
-
47
27
class PythonEngine (
48
28
private val methodUnderTest : PythonMethod ,
49
29
private val directoriesForSysPath : Set <String >,
@@ -55,6 +35,8 @@ class PythonEngine(
55
35
private val pythonTypeStorage : PythonTypeStorage ,
56
36
) {
57
37
38
+ private val cache = EvaluationCache ()
39
+
58
40
private fun suggestExecutionName (
59
41
description : PythonMethodDescription ,
60
42
executionResult : UtExecutionResult
@@ -146,109 +128,155 @@ class PythonEngine(
146
128
return ValidExecution (utFuzzedExecution)
147
129
}
148
130
149
- private fun constructEvaluationInput (arguments : List <PythonFuzzedValue >, additionalModules : List <String >): PythonCodeExecutor {
150
- val argumentValues = arguments.map { PythonTreeModel (it.tree, it.tree.type) }
151
-
152
- val (thisObject, modelList) =
153
- if (methodUnderTest.hasThisArgument)
154
- Pair (argumentValues[0 ], argumentValues.drop(1 ))
155
- else
156
- Pair (null , argumentValues)
157
-
158
- val argumentModules = argumentValues
159
- .flatMap { it.allContainingClassIds }
160
- .map { it.moduleName }
161
- .filterNot { it.startsWith(moduleToImport) }
162
- val localAdditionalModules = (additionalModules + argumentModules + moduleToImport).toSet()
163
-
164
- return PythonCodeExecutorImpl (
131
+ private fun constructEvaluationInput (pythonWorker : PythonWorker ): PythonCodeExecutor {
132
+ return PythonCodeSocketExecutor (
165
133
methodUnderTest,
166
- FunctionArguments (thisObject, methodUnderTest.thisObjectName, modelList, methodUnderTest.argumentsNames),
167
- argumentValues.map { FuzzedValue (it) },
168
134
moduleToImport,
169
- localAdditionalModules,
170
135
pythonPath,
171
136
directoriesForSysPath,
172
137
timeoutForRun,
138
+ pythonWorker,
173
139
)
174
140
}
175
141
176
142
fun fuzzing (parameters : List <Type >, isCancelled : () -> Boolean , until : Long ): Flow <FuzzingExecutionFeedback > = flow {
177
143
val additionalModules = parameters.flatMap { it.pythonModules() }
178
144
val coveredLines = initialCoveredLines.toMutableSet()
179
145
180
- suspend fun fuzzingResultHandler (description : PythonMethodDescription , arguments : List <PythonFuzzedValue >): PythonFeedback {
181
- val codeExecutor = constructEvaluationInput(arguments, additionalModules)
182
- return when (val evaluationResult = codeExecutor.run ()) {
183
- is PythonEvaluationError -> {
184
- val utError = UtError (
185
- " Error evaluation: ${evaluationResult.status} , ${evaluationResult.message} " ,
186
- Throwable (evaluationResult.stackTrace.joinToString(" \n " ))
187
- )
188
- logger.debug(evaluationResult.stackTrace.joinToString(" \n " ))
189
- emit(InvalidExecution (utError))
190
- PythonFeedback (control = Control .PASS )
191
- }
146
+ ServerSocket (0 ).use { serverSocket ->
147
+ logger.info { " Server port: ${serverSocket.localPort} " }
148
+ val manager = PythonWorkerManager (
149
+ serverSocket,
150
+ pythonPath,
151
+ until,
152
+ { constructEvaluationInput(it) },
153
+ timeoutForRun.toInt()
154
+ )
155
+ logger.info { " Executor manager was created successfully" }
192
156
193
- is PythonEvaluationTimeout -> {
194
- val utError = UtError (evaluationResult.message, Throwable ())
195
- emit(InvalidExecution (utError))
196
- PythonFeedback (control = Control .PASS )
197
- }
157
+ fun fuzzingResultHandler (
158
+ description : PythonMethodDescription ,
159
+ arguments : List <PythonFuzzedValue >
160
+ ): PythonExecutionResult ? {
161
+ val argumentValues = arguments.map { PythonTreeModel (it.tree, it.tree.type) }
162
+ logger.debug(argumentValues.map { it.tree } .toString())
163
+ val argumentModules = argumentValues
164
+ .flatMap { it.allContainingClassIds }
165
+ .map { it.moduleName }
166
+ .filterNot { it.startsWith(moduleToImport) }
167
+ val localAdditionalModules = (additionalModules + argumentModules + moduleToImport).toSet()
198
168
199
- is PythonEvaluationSuccess -> {
200
- val coveredInstructions = evaluationResult.coverage.coveredInstructions
201
- coveredInstructions.forEach { coveredLines.add(it.lineNumber) }
169
+ val (thisObject, modelList) =
170
+ if (methodUnderTest.hasThisArgument)
171
+ Pair (argumentValues[0 ], argumentValues.drop(1 ))
172
+ else
173
+ Pair (null , argumentValues)
174
+ val functionArguments = FunctionArguments (
175
+ thisObject,
176
+ methodUnderTest.thisObjectName,
177
+ modelList,
178
+ methodUnderTest.argumentsNames
179
+ )
180
+ try {
181
+ return when (val evaluationResult = manager.run (functionArguments, localAdditionalModules)) {
182
+ is PythonEvaluationError -> {
183
+ val utError = UtError (
184
+ " Error evaluation: ${evaluationResult.status} , ${evaluationResult.message} " ,
185
+ Throwable (evaluationResult.stackTrace.joinToString(" \n " ))
186
+ )
187
+ logger.debug(evaluationResult.stackTrace.joinToString(" \n " ))
188
+ PythonExecutionResult (InvalidExecution (utError), PythonFeedback (control = Control .PASS ))
189
+ }
202
190
203
- val summary = arguments
204
- .zip(methodUnderTest.arguments)
205
- .mapNotNull { it.first.summary?.replace(" %var%" , it.second.name) }
191
+ is PythonEvaluationTimeout -> {
192
+ val utError = UtError (evaluationResult.message, Throwable ())
193
+ PythonExecutionResult (InvalidExecution (utError), PythonFeedback (control = Control .PASS ))
194
+ }
206
195
207
- val hasThisObject = codeExecutor.methodArguments.thisObject != null
196
+ is PythonEvaluationSuccess -> {
197
+ val coveredInstructions = evaluationResult.coverage.coveredInstructions
198
+ coveredInstructions.forEach { coveredLines.add(it.lineNumber) }
208
199
209
- when (val result = handleSuccessResult(parameters, evaluationResult, description, hasThisObject, summary)) {
210
- is ValidExecution -> {
211
- logger.debug { arguments }
212
- val trieNode: Trie .Node <Instruction > = description.tracer.add(coveredInstructions)
213
- emit(result)
214
- PythonFeedback (control = Control .CONTINUE , result = trieNode)
215
- }
216
- is ArgumentsTypeErrorFeedback , is TypeErrorFeedback -> {
217
- emit(result)
218
- PythonFeedback (control = Control .PASS )
219
- }
220
- is InvalidExecution -> {
221
- emit(result)
222
- PythonFeedback (control = Control .CONTINUE )
200
+ val summary = arguments
201
+ .zip(methodUnderTest.arguments)
202
+ .mapNotNull { it.first.summary?.replace(" %var%" , it.second.name) }
203
+
204
+ val hasThisObject = methodUnderTest.hasThisArgument
205
+
206
+ when (val result = handleSuccessResult(
207
+ parameters,
208
+ evaluationResult,
209
+ description,
210
+ hasThisObject,
211
+ summary
212
+ )) {
213
+ is ValidExecution -> {
214
+ val trieNode: Trie .Node <Instruction > = description.tracer.add(coveredInstructions)
215
+ PythonExecutionResult (
216
+ result,
217
+ PythonFeedback (control = Control .CONTINUE , result = trieNode)
218
+ )
219
+ }
220
+
221
+ is ArgumentsTypeErrorFeedback , is TypeErrorFeedback -> {
222
+ PythonExecutionResult (result, PythonFeedback (control = Control .PASS ))
223
+ }
224
+
225
+ is InvalidExecution -> {
226
+ PythonExecutionResult (result, PythonFeedback (control = Control .CONTINUE ))
227
+ }
228
+ }
223
229
}
224
230
}
231
+ } catch (_: TimeoutException ) {
232
+ return null
225
233
}
226
234
}
227
- }
228
235
229
- val pmd = PythonMethodDescription (
230
- methodUnderTest.name,
231
- parameters,
232
- fuzzedConcreteValues,
233
- pythonTypeStorage,
234
- Trie (Instruction ::id)
235
- )
236
+ val pmd = PythonMethodDescription (
237
+ methodUnderTest.name,
238
+ parameters,
239
+ fuzzedConcreteValues,
240
+ pythonTypeStorage,
241
+ Trie (Instruction ::id)
242
+ )
236
243
237
- if (parameters.isEmpty()) {
238
- fuzzingResultHandler(pmd, emptyList())
239
- } else {
240
- PythonFuzzing (pmd.pythonTypeStorage) { description, arguments ->
241
- if (isCancelled()) {
242
- logger.info { " Fuzzing process was interrupted" }
243
- return @PythonFuzzing PythonFeedback (control = Control .STOP )
244
- }
245
- if (System .currentTimeMillis() >= until) {
246
- logger.info { " Fuzzing process was interrupted by timeout" }
247
- return @PythonFuzzing PythonFeedback (control = Control .STOP )
248
- }
244
+ if (parameters.isEmpty()) {
245
+ fuzzingResultHandler(pmd, emptyList())
246
+ manager.disconnect()
247
+ } else {
248
+ PythonFuzzing (pmd.pythonTypeStorage) { description, arguments ->
249
+ if (isCancelled()) {
250
+ logger.info { " Fuzzing process was interrupted" }
251
+ manager.disconnect()
252
+ return @PythonFuzzing PythonFeedback (control = Control .STOP )
253
+ }
254
+ if (System .currentTimeMillis() >= until) {
255
+ logger.info { " Fuzzing process was interrupted by timeout" }
256
+ manager.disconnect()
257
+ return @PythonFuzzing PythonFeedback (control = Control .STOP )
258
+ }
249
259
250
- return @PythonFuzzing fuzzingResultHandler(description, arguments)
251
- }.fuzz(pmd)
260
+ val pair = Pair (description, arguments.map { PythonTreeWrapper (it.tree) })
261
+ val mem = cache.get(pair)
262
+ if (mem != null ) {
263
+ logger.debug(" Repeat in fuzzing" )
264
+ emit(mem.fuzzingExecutionFeedback)
265
+ return @PythonFuzzing mem.fuzzingPlatformFeedback
266
+ }
267
+ val result = fuzzingResultHandler(description, arguments)
268
+ if (result == null ) { // timeout
269
+ logger.info { " Fuzzing process was interrupted by timeout" }
270
+ manager.disconnect()
271
+ return @PythonFuzzing PythonFeedback (control = Control .STOP )
272
+ }
273
+
274
+ cache.add(pair, result)
275
+ emit(result.fuzzingExecutionFeedback)
276
+ return @PythonFuzzing result.fuzzingPlatformFeedback
277
+
278
+ }.fuzz(pmd)
279
+ }
252
280
}
253
281
}
254
282
}
0 commit comments