Skip to content

Commit 2dc9600

Browse files
authored
Python fix and refactoring (#2509)
1 parent 870153f commit 2dc9600

File tree

14 files changed

+123
-85
lines changed

14 files changed

+123
-85
lines changed

utbot-cli-python/build.gradle

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
tasks.withType(KotlinCompile).configureEach {
24
kotlinOptions {
35
jvmTarget = JavaVersion.VERSION_17
46
freeCompilerArgs += ["-Xallow-result-return-type", "-Xsam-conversions=class"]
57
}
68
}
79

8-
tasks.withType(JavaCompile) {
10+
tasks.withType(JavaCompile).configureEach {
911
sourceCompatibility = JavaVersion.VERSION_1_8
1012
targetCompatibility = JavaVersion.VERSION_17
1113
}
@@ -16,22 +18,12 @@ configurations {
1618

1719
dependencies {
1820
implementation project(':utbot-framework')
19-
implementation project(':utbot-cli')
2021
implementation project(':utbot-python')
2122

22-
implementation group: 'org.mockito', name: 'mockito-core', version: mockitoVersion
23-
// Without this dependency testng tests do not run.
24-
implementation group: 'com.beust', name: 'jcommander', version: '1.48'
25-
implementation group: 'org.junit.platform', name: 'junit-platform-console-standalone', version: junit4PlatformVersion
2623
implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion
2724
implementation group: 'com.github.ajalt.clikt', name: 'clikt', version: cliktVersion
28-
implementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: junit5Version
29-
implementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: junit5Version
3025
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4j2Version
3126
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4j2Version
32-
implementation group: 'org.jacoco', name: 'org.jacoco.report', version: jacocoVersion
33-
//noinspection GroovyAssignabilityCheck
34-
fetchInstrumentationJar project(path: ':utbot-instrumentation', configuration:'instrumentationArchive')
3527
}
3628

3729
processResources {
@@ -40,7 +32,8 @@ processResources {
4032
}
4133
}
4234

43-
task createProperties(dependsOn: processResources) {
35+
tasks.register('createProperties') {
36+
dependsOn processResources
4437
doLast {
4538
new File("$buildDir/resources/main/version.properties").withWriter { w ->
4639
Properties properties = new Properties()

utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/Application.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import com.github.ajalt.clikt.parameters.options.default
66
import com.github.ajalt.clikt.parameters.options.option
77
import com.github.ajalt.clikt.parameters.options.versionOption
88
import com.github.ajalt.clikt.parameters.types.enum
9+
import mu.KotlinLogging
10+
import org.apache.logging.log4j.LogManager
11+
import org.apache.logging.log4j.core.config.Configurator
912
import org.slf4j.event.Level
10-
import org.utbot.cli.getVersion
11-
import org.utbot.cli.setVerbosity
13+
import java.util.*
1214
import kotlin.system.exitProcess
1315

16+
private val logger = KotlinLogging.logger {}
17+
1418
class UtBotPythonCli : CliktCommand(name = "UnitTestBot Python Command Line Interface") {
1519
private val verbosity by option("--verbosity", help = "Changes verbosity level, case insensitive")
1620
.enum<Level>(ignoreCase = true)
@@ -32,4 +36,20 @@ fun main(args: Array<String>) = try {
3236
} catch (ex: Throwable) {
3337
ex.printStackTrace()
3438
exitProcess(1)
35-
}
39+
}
40+
41+
fun setVerbosity(verbosity: Level) {
42+
Configurator.setAllLevels(LogManager.getRootLogger().name, level(verbosity))
43+
logger.debug { "Log Level changed to [$verbosity]" }
44+
}
45+
46+
private fun level(level: Level) = org.apache.logging.log4j.Level.toLevel(level.name)
47+
48+
fun getVersion(): String {
49+
val prop = Properties()
50+
Thread.currentThread().contextClassLoader.getResourceAsStream("version.properties")
51+
.use { stream ->
52+
prop.load(stream)
53+
}
54+
return prop.getProperty("version")
55+
}

utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogProcessor.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import com.jetbrains.python.psi.PyFile
2525
import com.jetbrains.python.psi.PyFunction
2626
import com.jetbrains.python.psi.resolve.QualifiedNameFinder
2727
import mu.KotlinLogging
28-
import org.jetbrains.kotlin.idea.util.module
29-
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
28+
import org.jetbrains.kotlin.idea.base.util.module
29+
import org.jetbrains.kotlin.idea.base.util.sdk
3030
import org.utbot.common.PathUtil.toPath
3131
import org.utbot.framework.plugin.api.util.LockFile
3232
import org.utbot.intellij.plugin.settings.Settings
@@ -183,7 +183,7 @@ object PythonDialogProcessor {
183183
.mapNotNull {
184184
val functionName = it.name ?: return@mapNotNull null
185185
val moduleFilename = it.containingFile.virtualFile?.canonicalPath ?: ""
186-
val containingClassId = it.containingClass?.name?.let{ cls -> PythonClassId(cls) }
186+
val containingClassId = it.containingClass?.qualifiedName?.let{ cls -> PythonClassId(cls) }
187187
PythonMethodHeader(
188188
functionName,
189189
moduleFilename,

utbot-python/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ val jacksonVersion: String? by rootProject
55
val ideType: String? by rootProject
66
val pythonCommunityPluginVersion: String? by rootProject
77
val pythonUltimatePluginVersion: String? by rootProject
8+
val log4j2Version: String? by rootProject
89

910
tasks {
1011
compileKotlin {
@@ -41,4 +42,6 @@ dependencies {
4142
implementation("org.functionaljava:functionaljava-quickcheck:5.0")
4243
implementation("org.functionaljava:functionaljava-java-core:5.0")
4344
implementation(group = "org.apache.commons", name = "commons-text", version = apacheCommonsTextVersion)
45+
implementation(group = "org.apache.logging.log4j", name = "log4j-core", version = log4j2Version)
46+
implementation(group = "org.apache.logging.log4j", name = "log4j-api", version = log4j2Version)
4447
}

utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class PythonEngine(
143143
types.joinToString { it.pythonTypeRepresentation() }
144144
}. Exception type: ${resultModel.type.name}"
145145

146-
logger.info(errorMessage)
146+
logger.debug { errorMessage }
147147
return TypeErrorFeedback(errorMessage)
148148
}
149149

@@ -269,14 +269,14 @@ class PythonEngine(
269269
}
270270
}
271271
} catch (_: TimeoutException) {
272-
logger.info { "Fuzzing process was interrupted by timeout" }
272+
logger.debug { "Fuzzing process was interrupted by timeout" }
273273
return null
274274
}
275275
}
276276

277277
fun fuzzing(parameters: List<Type>, isCancelled: () -> Boolean, until: Long): Flow<FuzzingExecutionFeedback> = flow {
278278
ServerSocket(0).use { serverSocket ->
279-
logger.info { "Server port: ${serverSocket.localPort}" }
279+
logger.debug { "Server port: ${serverSocket.localPort}" }
280280
val manager = try {
281281
PythonWorkerManager(
282282
serverSocket,
@@ -286,7 +286,7 @@ class PythonEngine(
286286
} catch (_: TimeoutException) {
287287
return@flow
288288
}
289-
logger.info { "Executor manager was created successfully" }
289+
logger.debug { "Executor manager was created successfully" }
290290

291291
val pmd = PythonMethodDescription(
292292
methodUnderTest.name,
@@ -307,18 +307,18 @@ class PythonEngine(
307307
try {
308308
PythonFuzzing(pmd.pythonTypeStorage) { description, arguments ->
309309
if (isCancelled()) {
310-
logger.info { "Fuzzing process was interrupted" }
310+
logger.debug { "Fuzzing process was interrupted" }
311311
manager.disconnect()
312312
return@PythonFuzzing PythonFeedback(control = Control.STOP)
313313
}
314314
if (System.currentTimeMillis() >= until) {
315-
logger.info { "Fuzzing process was interrupted by timeout" }
315+
logger.debug { "Fuzzing process was interrupted by timeout" }
316316
manager.disconnect()
317317
return@PythonFuzzing PythonFeedback(control = Control.STOP)
318318
}
319319

320320
if (arguments.any { PythonTree.containsFakeNode(it.tree) }) {
321-
logger.debug("FakeNode in Python model")
321+
logger.debug { "FakeNode in Python model" }
322322
emit(FakeNodeFeedback)
323323
return@PythonFuzzing PythonFeedback(control = Control.CONTINUE)
324324
}
@@ -341,7 +341,7 @@ class PythonEngine(
341341
return@PythonFuzzing result.fuzzingPlatformFeedback
342342
}.fuzz(pmd)
343343
} catch (_: NoSeedValueException) {
344-
logger.info { "Cannot fuzz values for types: ${parameters.map { it.pythonTypeRepresentation() }}" }
344+
logger.debug { "Cannot fuzz values for types: ${parameters.map { it.pythonTypeRepresentation() }}" }
345345
}
346346
}
347347
} finally {

utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.utbot.framework.plugin.api.UtError
77
import org.utbot.framework.plugin.api.UtExecution
88
import org.utbot.framework.plugin.api.UtExecutionSuccess
99
import org.utbot.python.framework.api.python.PythonUtExecution
10+
import org.utbot.python.framework.api.python.util.pythonStrClassId
1011
import org.utbot.python.fuzzing.*
1112
import org.utbot.python.newtyping.*
1213
import org.utbot.python.newtyping.ast.visitor.Visitor
@@ -137,10 +138,15 @@ class PythonTestCaseGenerator(
137138
var missingLines = initMissingLines
138139

139140
val (hintCollector, constantCollector) = constructCollectors(mypyStorage, typeStorage, method)
140-
val constants = constantCollector.result.map { (type, value) ->
141-
logger.debug { "Collected constant: ${type.pythonTypeRepresentation()}: $value" }
142-
PythonFuzzedConcreteValue(type, value)
143-
}
141+
val constants = constantCollector.result
142+
.mapNotNull { (type, value) ->
143+
if (type.pythonTypeName() == pythonStrClassId.name && value is String) {
144+
// Filter doctests
145+
if (value.contains(">>>")) return@mapNotNull null
146+
}
147+
logger.debug { "Collected constant: ${type.pythonTypeRepresentation()}: $value" }
148+
PythonFuzzedConcreteValue(type, value)
149+
}
144150

145151
inferAnnotations(
146152
method,
@@ -154,7 +160,7 @@ class PythonTestCaseGenerator(
154160
) { functionType ->
155161
val args = (functionType as FunctionType).arguments
156162

157-
logger.info { "Inferred annotations: ${args.joinToString { it.pythonTypeRepresentation() }}" }
163+
logger.debug { "Inferred annotations: ${args.joinToString { it.pythonTypeRepresentation() }}" }
158164

159165
val engine = PythonEngine(
160166
method,
@@ -222,7 +228,7 @@ class PythonTestCaseGenerator(
222228
val errors = mutableListOf<UtError>()
223229
val coveredLines = mutableSetOf<Int>()
224230

225-
logger.info("Start test generation for ${method.name}")
231+
logger.info { "Start test generation for ${method.name}" }
226232
try {
227233
val methodModifications = mutableSetOf<Pair<PythonMethod, String>>() // Set of pairs <PythonMethod, additionalVars>
228234

@@ -247,10 +253,10 @@ class PythonTestCaseGenerator(
247253
)
248254
}
249255
} catch (_: OutOfMemoryError) {
250-
logger.info { "Out of memory error. Stop test generation process" }
256+
logger.debug { "Out of memory error. Stop test generation process" }
251257
}
252258

253-
logger.info("Collect all test executions for ${method.name}")
259+
logger.info { "Collect all test executions for ${method.name}" }
254260
val (emptyCoverageExecutions, coverageExecutions) = executions.partition { it.coverage == null }
255261
val (successfulExecutions, failedExecutions) = coverageExecutions.partition { it.result is UtExecutionSuccess }
256262

utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ abstract class PythonTestGenerationProcessor {
156156
private fun collectImports(notEmptyTests: List<PythonTestSet>): Set<PythonImport> {
157157
val importParamModules = notEmptyTests.flatMap { testSet ->
158158
testSet.executions.flatMap { execution ->
159-
val params = (execution.stateAfter.parameters + execution.stateBefore.parameters).toMutableSet()
159+
val params = (execution.stateAfter.parameters + execution.stateBefore.parameters).toMutableList()
160160
val self = mutableListOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance)
161161
if (execution is PythonUtExecution) {
162162
params.addAll(execution.stateInit.parameters)
@@ -172,7 +172,7 @@ abstract class PythonTestGenerationProcessor {
172172
}
173173
}
174174
}
175-
}
175+
}.toSet()
176176
val importResultModules = notEmptyTests.flatMap { testSet ->
177177
testSet.executions.mapNotNull { execution ->
178178
if (execution.result is UtExecutionSuccess) {
@@ -185,7 +185,7 @@ abstract class PythonTestGenerationProcessor {
185185
}
186186
} else null
187187
}.flatten()
188-
}
188+
}.toSet()
189189
val rootModule = configuration.testFileInformation.moduleName.split(".").first()
190190
val testRootModule = PythonUserImport(importName_ = rootModule)
191191
val sysImport = PythonSystemImport("sys")

utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCodeSocketExecutor.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ class PythonCodeSocketExecutor(
100100
try {
101101
pythonWorker.sendData(message)
102102
} catch (_: SocketException) {
103-
logger.info { "Send data error" }
104103
return parseExecutionResult(FailExecution("Send data error"))
105104
}
106105

utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonCoverageReceiver.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@ import java.io.IOException
55
import java.net.DatagramPacket
66
import java.net.DatagramSocket
77
import java.net.SocketException
8+
import java.net.SocketTimeoutException
9+
import kotlin.math.max
810

911
class PythonCoverageReceiver(
10-
private val until: Long,
12+
val until: Long,
1113
) : Thread() {
1214
val coverageStorage = mutableMapOf<String, MutableSet<Int>>()
1315
private val socket = DatagramSocket()
1416
private val logger = KotlinLogging.logger {}
1517

18+
init {
19+
updateSoTimeout()
20+
}
21+
22+
private fun updateSoTimeout() {
23+
socket.soTimeout = max((until - System.currentTimeMillis()).toInt(), 0)
24+
}
25+
1626
fun address(): Pair<String, String> {
1727
return "localhost" to socket.localPort.toString()
1828
}
@@ -24,18 +34,21 @@ class PythonCoverageReceiver(
2434

2535
override fun run() {
2636
try {
27-
while (System.currentTimeMillis() < until) {
37+
while (true) {
38+
updateSoTimeout()
2839
val buf = ByteArray(256)
2940
val request = DatagramPacket(buf, buf.size)
3041
socket.receive(request)
3142
val (id, line) = request.data.decodeToString().take(request.length).split(":")
3243
val lineNumber = line.toInt()
33-
coverageStorage.getOrPut(id) { mutableSetOf() } .add(lineNumber)
44+
coverageStorage.getOrPut(id) { mutableSetOf() }.add(lineNumber)
3445
}
3546
} catch (ex: SocketException) {
36-
logger.error(ex.message)
47+
logger.debug { ex.message }
3748
} catch (ex: IOException) {
38-
logger.error("IO error: " + ex.message)
49+
logger.debug { "IO error: " + ex.message }
50+
} catch (ex: SocketTimeoutException) {
51+
logger.debug { "IO error: " + ex.message }
3952
}
4053
}
4154
}

utbot-python/src/main/kotlin/org/utbot/python/evaluation/PythonWorkerManager.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import java.lang.Long.max
1010
import java.net.ServerSocket
1111
import java.net.Socket
1212
import java.net.SocketTimeoutException
13+
import org.apache.logging.log4j.LogManager
1314

1415
private val logger = KotlinLogging.logger {}
1516

@@ -19,8 +20,6 @@ class PythonWorkerManager(
1920
val until: Long,
2021
val pythonCodeExecutorConstructor: (PythonWorker) -> PythonCodeExecutor,
2122
) {
22-
private val logfile = TemporaryFileManager.createTemporaryFile("","utbot_executor.log", "log", true)
23-
2423
var timeout: Long = 0
2524
lateinit var process: Process
2625
private lateinit var workerSocket: Socket
@@ -35,6 +34,10 @@ class PythonWorkerManager(
3534

3635
private fun connect() {
3736
val processStartTime = System.currentTimeMillis()
37+
if (this::process.isInitialized && process.isAlive) {
38+
process.destroy()
39+
}
40+
val logLevel = LogManager.getRootLogger().level.name()
3841
process = startProcess(listOf(
3942
pythonPath,
4043
"-m", "utbot_executor",
@@ -43,15 +46,18 @@ class PythonWorkerManager(
4346
coverageReceiver.address().first,
4447
coverageReceiver.address().second,
4548
"--logfile", logfile.absolutePath,
46-
"--loglevel", "INFO", // "DEBUG", "INFO", "WARNING", "ERROR"
49+
"--loglevel", logLevel, // "DEBUG", "INFO", "WARNING", "ERROR"
4750
))
4851
timeout = max(until - processStartTime, 0)
52+
if (this::workerSocket.isInitialized && !workerSocket.isClosed) {
53+
workerSocket.close()
54+
}
4955
workerSocket = try {
5056
serverSocket.soTimeout = timeout.toInt()
5157
serverSocket.accept()
5258
} catch (e: SocketTimeoutException) {
5359
val result = getResult(process, max(until - processStartTime, 0))
54-
logger.info("utbot_executor exit value: ${result.exitValue}. stderr: ${result.stderr}, stdout: ${result.stdout}.")
60+
logger.debug { "utbot_executor exit value: ${result.exitValue}. stderr: ${result.stderr}, stdout: ${result.stdout}." }
5561
process.destroy()
5662
throw TimeoutException("Worker not connected")
5763
}
@@ -111,4 +117,8 @@ class PythonWorkerManager(
111117
}
112118
return evaluationResult
113119
}
120+
121+
companion object {
122+
val logfile = TemporaryFileManager.createTemporaryFile("", "utbot_executor.log", "log", true)
123+
}
114124
}

0 commit comments

Comments
 (0)