From 1e34350b1c94cd267bd79406788e3df46259d2cf Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 18 Nov 2022 12:33:46 +0300 Subject: [PATCH 1/7] [rd-refactoring] Multiple refactorings and enhacements for process starting. Removed UtSettings.logConcreteExecutionErrors and UtSettings.engineProcessLogLevel switches. Logs for engine and concrete executor processes are always enabled. Switched log configuration to utbot-intellij/log4j2.xml. Use UtSettings#getIdeaProcessLogConfigFile from ~/.utbot/settings.properties to configure logs for engine and concrete executor processes without rebuilding plugin. --- .../org/utbot/visual/AbstractHtmlReport.kt | 3 +- .../main/kotlin/org/utbot/common/JvmUtil.kt | 5 +- .../main/kotlin/org/utbot/common/Logging.kt | 9 +- .../main/kotlin/org/utbot/common/PathUtil.kt | 6 + .../kotlin/org/utbot/framework/UtSettings.kt | 15 +- .../framework/process/OpenModulesContainer.kt | 3 +- .../src/test/resources/log4j2.xml | 4 +- .../framework/plugin/api/TestCaseGenerator.kt | 2 +- .../org/utbot/framework/process/EngineMain.kt | 36 +- .../utbot/framework/process/RdInstrumenter.kt | 28 +- .../framework/process/RdSettingsContainer.kt | 21 +- .../utbot/instrumentation/ConcreteExecutor.kt | 8 +- .../coverage/CoverageInstrumentation.kt | 4 +- .../instrumentation/process/ChildProcess.kt | 18 +- .../process/ChildProcessRunner.kt | 26 +- .../rd/UtInstrumentationProcess.kt | 34 +- .../org/utbot/intellij/plugin/UtbotBundle.kt | 20 + .../generator/CodeGenerationController.kt | 58 +- .../generator/UtTestsDialogProcessor.kt | 46 +- .../intellij/plugin/process/EngineProcess.kt | 544 +++++++++--------- .../intellij/plugin/util/IdeaThreadingUtil.kt | 23 + .../resources/bundles/UtbotBundle.properties | 10 + utbot-intellij/src/main/resources/log4j2.xml | 22 +- .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 30 +- .../org/utbot/rd/ProcessWithRdServer.kt | 9 - .../kotlin/org/utbot/rd/UtRdCoroutineScope.kt | 3 + .../src/main/kotlin/org/utbot/rd/UtRdUtil.kt | 55 +- .../org/utbot/rd/loggers/UtRdConsoleLogger.kt | 21 +- .../org/utbot/rd/loggers/UtRdKLogger.kt | 15 +- .../utbot/rd/loggers/UtRdKLoggerFactory.kt | 2 +- 30 files changed, 557 insertions(+), 523 deletions(-) create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt create mode 100644 utbot-intellij/src/main/resources/bundles/UtbotBundle.properties diff --git a/utbot-analytics/src/main/kotlin/org/utbot/visual/AbstractHtmlReport.kt b/utbot-analytics/src/main/kotlin/org/utbot/visual/AbstractHtmlReport.kt index 9f1673a57e..ce7ff03e6f 100644 --- a/utbot-analytics/src/main/kotlin/org/utbot/visual/AbstractHtmlReport.kt +++ b/utbot-analytics/src/main/kotlin/org/utbot/visual/AbstractHtmlReport.kt @@ -1,5 +1,6 @@ package org.utbot.visual +import org.utbot.common.dateTimeFormatter import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -7,8 +8,6 @@ import java.time.format.DateTimeFormatter abstract class AbstractHtmlReport(bodyWidth: Int = 600) { val builder = HtmlBuilder(bodyMaxWidth = bodyWidth) - private val dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy_HH-mm-ss") - private fun nameWithDate() = "logs/Report_" + dateTimeFormatter.format(LocalDateTime.now()) + ".html" diff --git a/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt index 86868349ec..ab428b7e95 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt @@ -4,4 +4,7 @@ private val javaSpecificationVersion = System.getProperty("java.specification.ve val isJvm8 = javaSpecificationVersion.equals("1.8") val isJvm9Plus = !javaSpecificationVersion.contains(".") && javaSpecificationVersion.toInt() >= 9 -fun osSpecificJavaExecutable() = if (isWindows) "javaw" else "java" \ No newline at end of file +fun osSpecificJavaExecutable() = if (isWindows) "javaw" else "java" + +const val engineProcessDebugPort = 5005 +const val childProcessDebugPort = 5006 \ No newline at end of file diff --git a/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt b/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt index 381d7328b9..99d1b7a7a2 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt @@ -3,7 +3,8 @@ package org.utbot.common import mu.KLogger import java.time.format.DateTimeFormatter -val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") +val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") +val dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy_HH-mm-ss") class LoggerWithLogMethod(val logger: KLogger, val logMethod: (() -> Any?) -> Unit) @@ -11,18 +12,12 @@ fun KLogger.info(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::info) fun KLogger.debug(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::debug) fun KLogger.trace(): LoggerWithLogMethod = LoggerWithLogMethod(this, this::trace) - -/** - * - */ fun elapsedSecFrom(startNano: Long): String { val elapsedNano = System.nanoTime() - startNano val elapsedS = elapsedNano.toDouble() / 1_000_000_000 return String.format("%.3f", elapsedS) + " sec" } - - /** * Structured logging. * diff --git a/utbot-core/src/main/kotlin/org/utbot/common/PathUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/PathUtil.kt index 762eff83e7..b74cb39a56 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/PathUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/PathUtil.kt @@ -10,6 +10,12 @@ import java.util.* object PathUtil { + fun String.toPathOrNull(): Path? = try { + Paths.get(this) + } catch (e: Throwable) { + null + } + /** * Creates a Path from the String. */ diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 2ac46750e2..8539d900b4 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -264,24 +264,17 @@ object UtSettings : AbstractSettings( DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS ) - /** - * Log level for engine process, which started in idea on generate tests action. - */ - var engineProcessLogLevel by getEnumProperty(LogLevel.Info) - /** * Log level for concrete executor process. */ var childProcessLogLevel by getEnumProperty(LogLevel.Info) /** - * Determines whether should errors from a child process be written to a log file or suppressed. - * Note: being enabled, this option can highly increase disk usage when using ContestEstimator. - * - * False by default (for saving disk space). + * Path to custom log4j2 configuration file for EngineProcess. + * By default utbot-intellij/src/main/resources/log4j2.xml is used. + * Also default value is used if provided value is not a file. */ - var logConcreteExecutionErrors by getBooleanProperty(false) - + var ideaProcessLogConfigFile by getStringProperty("") /** * Property useful only for idea diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt index 650e7d40c4..8eef6dbb03 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt @@ -4,10 +4,9 @@ import org.utbot.framework.plugin.services.JdkInfoService object OpenModulesContainer { private val modulesContainer: List - val javaVersionSpecificArguments: List + val javaVersionSpecificArguments: List? get() = modulesContainer .takeIf { JdkInfoService.provide().version > 8 } - ?: emptyList() init { modulesContainer = buildList { diff --git a/utbot-framework-test/src/test/resources/log4j2.xml b/utbot-framework-test/src/test/resources/log4j2.xml index 11a2d0701c..ac3d2f2abf 100644 --- a/utbot-framework-test/src/test/resources/log4j2.xml +++ b/utbot-framework-test/src/test/resources/log4j2.xml @@ -7,12 +7,12 @@ filePattern="logs/framework-%d{MM-dd-yyyy}.log.gz" ignoreExceptions="false"> - + - + diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index ec51997b6b..1acbaa4bd3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -204,7 +204,7 @@ open class TestCaseGenerator( method2controller.values.forEach { it.paused = true } controller.paused = false - logger.info { "|> Resuming method $method" } + logger.info { "Resuming method $method" } val startTime = System.currentTimeMillis() while (controller.job!!.isActive && (System.currentTimeMillis() - startTime) < executionTimeEstimator.timeslotForOneToplevelMethodTraversalInMillis diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt index 4b45922d60..c92cf99ddb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt @@ -32,7 +32,7 @@ import org.utbot.framework.process.generated.* import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.util.KryoHelper -import org.utbot.rd.CallsSynchronizer +import org.utbot.rd.IdleWatchdog import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.findRdPort import org.utbot.rd.loggers.UtRdKLoggerFactory @@ -56,11 +56,7 @@ suspend fun main(args: Array) = runBlocking { Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { - settingsModel - rdSourceFindingStrategy - rdInstrumenterAdapter - - AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol)) + AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel)) val kryoHelper = KryoHelper(lifetime) engineProcessModel.setup(kryoHelper, it, protocol) } @@ -74,18 +70,16 @@ private val testSets: MutableMap> = mutableMapOf() private val testGenerationReports: MutableList = mutableListOf() private var idCounter: Long = 0 -private fun EngineProcessModel.setup( - kryoHelper: KryoHelper, synchronizer: CallsSynchronizer, realProtocol: IProtocol -) { +private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog, realProtocol: IProtocol) { val model = this - synchronizer.measureExecutionForTermination(setupUtContext) { params -> + watchdog.wrapActiveCall(setupUtContext) { params -> UtContext.setUtContext(UtContext(URLClassLoader(params.classpathForUrlsClassloader.map { File(it).toURI().toURL() }.toTypedArray()))) } - synchronizer.measureExecutionForTermination(createTestGenerator) { params -> + watchdog.wrapActiveCall(createTestGenerator) { params -> AnalyticsConfigureUtil.configureML() - Instrumenter.adapter = RdInstrumenter(realProtocol) + Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter) testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, classpath = params.classpath, dependencyPaths = params.dependencyPaths, @@ -96,7 +90,7 @@ private fun EngineProcessModel.setup( } }) } - synchronizer.measureExecutionForTermination(generate) { params -> + watchdog.wrapActiveCall(generate) { params -> val mockFrameworkInstalled = params.mockInstalled val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers)) if (!mockFrameworkInstalled) { @@ -126,7 +120,7 @@ private fun EngineProcessModel.setup( testSets[id] = result GenerateResult(result.size, id) } - synchronizer.measureExecutionForTermination(render) { params -> + watchdog.wrapActiveCall(render) { params -> val testFramework = testFrameworkByName(params.testFramework) val staticMocking = if (params.staticsMocking.startsWith("No")) { NoStaticMocking @@ -157,11 +151,11 @@ private fun EngineProcessModel.setup( RenderResult(it.generatedCode, it.utilClassKind?.javaClass?.simpleName) } } - synchronizer.measureExecutionForTermination(stopProcess) { synchronizer.stopProtocol() } - synchronizer.measureExecutionForTermination(obtainClassId) { canonicalName -> + watchdog.wrapActiveCall(stopProcess) { watchdog.stopProtocol() } + watchdog.wrapActiveCall(obtainClassId) { canonicalName -> kryoHelper.writeObject(UtContext.currentContext()!!.classLoader.loadClass(canonicalName).id) } - synchronizer.measureExecutionForTermination(findMethodsInClassMatchingSelected) { params -> + watchdog.wrapActiveCall(findMethodsInClassMatchingSelected) { params -> val classId = kryoHelper.readObject(params.classId) val selectedSignatures = params.signatures.map { Signature(it.name, it.parametersTypes) } FindMethodsInClassMatchingSelectedResult(kryoHelper.writeObject(classId.jClass.allNestedClasses.flatMap { clazz -> @@ -170,7 +164,7 @@ private fun EngineProcessModel.setup( .map { it.executableId } })) } - synchronizer.measureExecutionForTermination(findMethodParamNames) { params -> + watchdog.wrapActiveCall(findMethodParamNames) { params -> val classId = kryoHelper.readObject(params.classId) val bySignature = kryoHelper.readObject>>(params.bySignature) FindMethodParamNamesResult(kryoHelper.writeObject( @@ -179,7 +173,7 @@ private fun EngineProcessModel.setup( .toMap() )) } - synchronizer.measureExecutionForTermination(writeSarifReport) { params -> + watchdog.wrapActiveCall(writeSarifReport) { params -> val reportFilePath = Paths.get(params.reportFilePath) reportFilePath.parent.toFile().mkdirs() val sarifReport = SarifReport( @@ -190,12 +184,12 @@ private fun EngineProcessModel.setup( reportFilePath.toFile().writeText(sarifReport) sarifReport } - synchronizer.measureExecutionForTermination(generateTestReport) { params -> + watchdog.wrapActiveCall(generateTestReport) { params -> val eventLogMessage = params.eventLogMessage val testPackageName: String? = params.testPackageName var hasWarnings = false val reports = testGenerationReports - if (reports.isEmpty()) return@measureExecutionForTermination GenerateTestReportResult("No tests were generated", null, true) + if (reports.isEmpty()) return@wrapActiveCall GenerateTestReportResult("No tests were generated", null, true) val isMultiPackage = params.isMultiPackage val (notifyMessage, statistics) = if (reports.size == 1) { val report = reports.first() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt index a53c851a5e..bb5aee3866 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt @@ -1,36 +1,30 @@ package org.utbot.framework.process -import com.jetbrains.rd.framework.IProtocol import kotlinx.coroutines.runBlocking import mu.KotlinLogging +import org.utbot.common.logException import org.utbot.framework.process.generated.ComputeSourceFileByClassArguments -import org.utbot.framework.process.generated.rdInstrumenterAdapter +import org.utbot.framework.process.generated.RdInstrumenterAdapter import org.utbot.instrumentation.instrumentation.instrumenter.InstrumenterAdapter +import org.utbot.rd.startBlocking import java.io.File import java.nio.file.Path private val logger = KotlinLogging.logger { } -class RdInstrumenter(private val protocol: IProtocol): InstrumenterAdapter() { +class RdInstrumenter(private val rdInstrumenterAdapter: RdInstrumenterAdapter) : InstrumenterAdapter() { override fun computeSourceFileByClass( className: String, packageName: String?, directoryToSearchRecursively: Path - ): File? = runBlocking { + ): File? { logger.debug { "starting computeSourceFileByClass with classname - $className" } - val result = try { - protocol.rdInstrumenterAdapter.computeSourceFileByClass.startSuspending( - ComputeSourceFileByClassArguments( - className, - packageName - ) - ) - } - catch(e: Exception) { - logger.error(e) { "error during computeSourceFileByClass" } - throw e + val result = logger.logException { + val arguments = ComputeSourceFileByClassArguments(className, packageName) + + rdInstrumenterAdapter.computeSourceFileByClass.startBlocking(arguments) } - logger.debug { "computeSourceFileByClass result for $className from idea: $result"} - return@runBlocking result?.let { File(it) } + logger.debug { "computeSourceFileByClass result for $className from idea: $result" } + return result?.let { File(it) } } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt index 4566dba88c..11d3a4ac62 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt @@ -1,42 +1,43 @@ package org.utbot.framework.process -import com.jetbrains.rd.framework.IProtocol import kotlinx.coroutines.runBlocking import mu.KLogger import org.utbot.common.SettingsContainer import org.utbot.common.SettingsContainerFactory import org.utbot.framework.process.generated.SettingForArgument -import org.utbot.framework.process.generated.settingsModel +import org.utbot.framework.process.generated.SettingsModel +import org.utbot.rd.startBlocking import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -class RdSettingsContainerFactory(private val protocol: IProtocol) : SettingsContainerFactory { +class RdSettingsContainerFactory(private val settingsModel: SettingsModel) : SettingsContainerFactory { override fun createSettingsContainer( logger: KLogger, defaultKeyForSettingsPath: String, defaultSettingsPath: String? ): SettingsContainer { - return RdSettingsContainer(logger, defaultKeyForSettingsPath, protocol) + return RdSettingsContainer(logger, defaultKeyForSettingsPath, settingsModel) } } -class RdSettingsContainer(val logger: KLogger, val key: String, val protocol: IProtocol): SettingsContainer { +class RdSettingsContainer(val logger: KLogger, val key: String, val settingsModel: SettingsModel) : SettingsContainer { override fun settingFor( defaultValue: T, converter: (String) -> T ): PropertyDelegateProvider> { - return PropertyDelegateProvider { _, prop -> - object: ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking { - return@runBlocking protocol.settingsModel.settingFor.startSuspending(SettingForArgument(key, property.name)).value?.let { + return PropertyDelegateProvider { _, _ -> + object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + val params = SettingForArgument(key, property.name) + return settingsModel.settingFor.startBlocking(params).value?.let { converter(it) } ?: defaultValue } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - throw NotImplementedError("Setting properties from child process not supported") + throw NotImplementedError("Setting properties allowed only from plugin process") } } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index 64a963fd35..e911faf312 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -244,7 +244,7 @@ class ConcreteExecutor> p val parametersByteArray = kryoHelper.writeObject(parameters) val params = InvokeMethodCommandParams(className, signature, argumentsByteArray, parametersByteArray) - val ba = protocolModel.invokeMethodCommand.startSuspending(lifetime, params).result + val ba = chidlProcessModel.invokeMethodCommand.startSuspending(lifetime, params).result kryoHelper.readObject(ba) } } catch (e: Throwable) { @@ -284,7 +284,7 @@ class ConcreteExecutor> p if (alive) { try { processInstance?.run { - protocolModel.stopProcess.start(lifetime, Unit) + chidlProcessModel.stopProcess.start(lifetime, Unit) } } catch (_: Exception) {} processInstance = null @@ -298,7 +298,7 @@ class ConcreteExecutor> p fun ConcreteExecutor<*,*>.warmup() = runBlocking { withProcess { - protocolModel.warmup.start(lifetime, Unit) + chidlProcessModel.warmup.start(lifetime, Unit) } } @@ -310,7 +310,7 @@ fun ConcreteExecutor<*, *>.computeStaticField(fieldId: FieldId): Result = val fieldIdSerialized = kryoHelper.writeObject(fieldId) val params = ComputeStaticFieldParams(fieldIdSerialized) - val result = protocolModel.computeStaticField.startSuspending(lifetime, params) + val result = chidlProcessModel.computeStaticField.startSuspending(lifetime, params) kryoHelper.readObject(result.result) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt index 1617cd4e21..49d4c5026e 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt @@ -10,11 +10,9 @@ import org.utbot.instrumentation.instrumentation.InvokeWithStaticsInstrumentatio import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.rd.generated.CollectCoverageParams import org.utbot.instrumentation.util.CastProbesArrayException -import org.utbot.instrumentation.util.ChildProcessError import org.utbot.instrumentation.util.NoProbesArrayException import java.security.ProtectionDomain import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.util.jField data class CoverageInfo( val methodToInstrRange: Map, @@ -105,6 +103,6 @@ fun ConcreteExecutor, CoverageInstrumentation>.collectCoverage(clazz: withProcess { val clazzByteArray = kryoHelper.writeObject(clazz) - kryoHelper.readObject(protocolModel.collectCoverage.startSuspending(lifetime, CollectCoverageParams(clazzByteArray)).coverageInfo) + kryoHelper.readObject(chidlProcessModel.collectCoverage.startSuspending(lifetime, CollectCoverageParams(clazzByteArray)).coverageInfo) } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt index cd535fd128..faff4abcf1 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt @@ -13,7 +13,7 @@ import org.utbot.instrumentation.rd.generated.CollectCoverageResult import org.utbot.instrumentation.rd.generated.InvokeMethodCommandResult import org.utbot.instrumentation.rd.generated.childProcessModel import org.utbot.instrumentation.util.KryoHelper -import org.utbot.rd.CallsSynchronizer +import org.utbot.rd.IdleWatchdog import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.findRdPort import org.utbot.rd.loggers.UtRdConsoleLoggerFactory @@ -110,15 +110,15 @@ private lateinit var pathsToUserClasses: Set private lateinit var pathsToDependencyClasses: Set private lateinit var instrumentation: Instrumentation<*> -private fun ChildProcessModel.setup(kryoHelper: KryoHelper, synchronizer: CallsSynchronizer) { - synchronizer.measureExecutionForTermination(warmup) { +private fun ChildProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog) { + watchdog.wrapActiveCall(warmup) { logger.debug { "received warmup request" } val time = measureTimeMillis { HandlerClassesLoader.scanForClasses("").toList() // here we transform classes } logger.debug { "warmup finished in $time ms" } } - synchronizer.measureExecutionForTermination(invokeMethodCommand) { params -> + watchdog.wrapActiveCall(invokeMethodCommand) { params -> logger.debug { "received invokeMethod request: ${params.classname}, ${params.signature}" } val clazz = HandlerClassesLoader.loadClass(params.classname) val res = kotlin.runCatching { @@ -138,7 +138,7 @@ private fun ChildProcessModel.setup(kryoHelper: KryoHelper, synchronizer: CallsS throw it } } - synchronizer.measureExecutionForTermination(setInstrumentation) { params -> + watchdog.wrapActiveCall(setInstrumentation) { params -> logger.debug { "setInstrumentation request" } instrumentation = kryoHelper.readObject(params.instrumentation) logger.trace { "instrumentation - ${instrumentation.javaClass.name} " } @@ -146,7 +146,7 @@ private fun ChildProcessModel.setup(kryoHelper: KryoHelper, synchronizer: CallsS Agent.dynamicClassTransformer.addUserPaths(pathsToUserClasses) instrumentation.init(pathsToUserClasses) } - synchronizer.measureExecutionForTermination(addPaths) { params -> + watchdog.wrapActiveCall(addPaths) { params -> logger.debug { "addPaths request" } pathsToUserClasses = params.pathsToUserClasses.split(File.pathSeparatorChar).toSet() pathsToDependencyClasses = params.pathsToDependencyClasses.split(File.pathSeparatorChar).toSet() @@ -155,11 +155,11 @@ private fun ChildProcessModel.setup(kryoHelper: KryoHelper, synchronizer: CallsS kryoHelper.setKryoClassLoader(HandlerClassesLoader) // Now kryo will use our classloader when it encounters unregistered class. UtContext.setUtContext(UtContext(HandlerClassesLoader)) } - synchronizer.measureExecutionForTermination(stopProcess) { + watchdog.wrapActiveCall(stopProcess) { logger.debug { "stop request" } - synchronizer.stopProtocol() + watchdog.stopProtocol() } - synchronizer.measureExecutionForTermination(collectCoverage) { params -> + watchdog.wrapActiveCall(collectCoverage) { params -> logger.debug { "collect coverage request" } val anyClass: Class<*> = kryoHelper.readObject(params.clazz) logger.debug { "class - ${anyClass.name}" } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt index 1dafdef084..053b9ec5e2 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt @@ -12,16 +12,15 @@ import org.utbot.instrumentation.Settings import org.utbot.instrumentation.agent.DynamicClassTransformer import org.utbot.rd.rdPortArgument import java.io.File +import java.time.LocalDateTime import kotlin.random.Random private val logger = KotlinLogging.logger {} class ChildProcessRunner { - private val id = Random.nextLong() - private var processSeqN = 0 private val cmds: List by lazy { val debugCmd = listOfNotNull(DEBUG_RUN_CMD.takeIf { UtSettings.runChildProcessWithDebug }) - val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments + val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments ?: emptyList() val pathToJava = JdkInfoService.provide().path listOf(pathToJava.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}").toString()) + @@ -36,12 +35,10 @@ class ChildProcessRunner { val portArgument = rdPortArgument(rdPort) logger.debug { "Starting child process: ${cmds.joinToString(" ")} $portArgument" } - processSeqN++ - if (UtSettings.logConcreteExecutionErrors) { - UT_BOT_TEMP_DIR.mkdirs() - errorLogFile = File(UT_BOT_TEMP_DIR, "${id}-${processSeqN}.log") - } + UT_BOT_TEMP_DIR.mkdirs() + val formatted = dateTimeFormatter.format(LocalDateTime.now()) + errorLogFile = File(UT_BOT_TEMP_DIR, "$formatted.log") val directory = WorkingDirService.provide().toFile() val commandsWithOptions = buildList { @@ -49,9 +46,7 @@ class ChildProcessRunner { if (!UtSettings.useSandbox) { add(DISABLE_SANDBOX_OPTION) } - if (UtSettings.logConcreteExecutionErrors) { - add(logLevelArgument(UtSettings.childProcessLogLevel)) - } + add(logLevelArgument(UtSettings.childProcessLogLevel)) add(portArgument) } @@ -60,11 +55,8 @@ class ChildProcessRunner { .directory(directory) return processBuilder.start().also { - logger.info { "Process started with PID=${it.getPid}" } - - if (UtSettings.logConcreteExecutionErrors) { - logger.info { "Child process error log: ${errorLogFile.absolutePath}" } - } + logger.info { "Child process started with PID=${it.getPid}" } + logger.info { "Child process log file: ${errorLogFile.absolutePath}" } } } @@ -73,7 +65,7 @@ class ChildProcessRunner { private const val ERRORS_FILE_PREFIX = "utbot-childprocess-errors" private const val INSTRUMENTATION_LIB = "lib" - private const val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5006" + private const val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=$childProcessDebugPort" private val UT_BOT_TEMP_DIR: File = File(utBotTempDirectory.toFile(), ERRORS_FILE_PREFIX) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt index ba7d09d62e..f9c99e8178 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt @@ -10,6 +10,7 @@ import org.utbot.instrumentation.rd.generated.SetInstrumentationParams import org.utbot.instrumentation.rd.generated.childProcessModel import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException @@ -27,23 +28,22 @@ class UtInstrumentationProcess private constructor( val kryoHelper = KryoHelper(lifetime.createNested()).apply { classLoader?.let { setKryoClassLoader(it) } } - val protocolModel: ChildProcessModel - get() = protocol.childProcessModel + val chidlProcessModel: ChildProcessModel = onSchedulerBlocking { protocol.childProcessModel } companion object { - private suspend fun > invokeImpl( - lifetime: Lifetime, + suspend operator fun > invoke( + parent: Lifetime, childProcessRunner: ChildProcessRunner, instrumentation: TInstrumentation, pathsToUserClasses: String, pathsToDependencyClasses: String, classLoader: ClassLoader? - ): UtInstrumentationProcess { + ): UtInstrumentationProcess = parent.createNested().terminateOnException { lifetime -> val rdProcess: ProcessWithRdServer = startUtProcessWithRdServer( lifetime = lifetime ) { childProcessRunner.start(it) - }.initModels { childProcessModel }.awaitSignal() + }.awaitSignal() logger.trace("rd process started") @@ -57,7 +57,7 @@ class UtInstrumentationProcess private constructor( } logger.trace("sending add paths") - proc.protocolModel.addPaths.startSuspending( + proc.chidlProcessModel.addPaths.startSuspending( proc.lifetime, AddPathsParams( pathsToUserClasses, pathsToDependencyClasses @@ -65,7 +65,7 @@ class UtInstrumentationProcess private constructor( ) logger.trace("sending instrumentation") - proc.protocolModel.setInstrumentation.startSuspending( + proc.chidlProcessModel.setInstrumentation.startSuspending( proc.lifetime, SetInstrumentationParams( proc.kryoHelper.writeObject(instrumentation) ) @@ -74,23 +74,5 @@ class UtInstrumentationProcess private constructor( return proc } - - suspend operator fun > invoke( - lifetime: Lifetime, - childProcessRunner: ChildProcessRunner, - instrumentation: TInstrumentation, - pathsToUserClasses: String, - pathsToDependencyClasses: String, - classLoader: ClassLoader? - ): UtInstrumentationProcess = lifetime.createNested().terminateOnException { - invokeImpl( - it, - childProcessRunner, - instrumentation, - pathsToUserClasses, - pathsToDependencyClasses, - classLoader - ) - } } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt new file mode 100644 index 0000000000..8070573aa2 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt @@ -0,0 +1,20 @@ +package org.utbot.intellij.plugin + +import com.intellij.DynamicBundle +import org.jetbrains.annotations.NonNls +import org.jetbrains.annotations.PropertyKey + +class UtbotBundle : DynamicBundle(BUNDLE) { + companion object { + @NonNls + private const val BUNDLE = "bundles.UtbotBundle" + private val INSTANCE: UtbotBundle = UtbotBundle() + + fun message( + @PropertyKey(resourceBundle = BUNDLE) key: String, + vararg params: Any + ): String { + return INSTANCE.getMessage(key, *params) + } + } +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt index 20ee983d00..7ab9f3f85e 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt @@ -8,6 +8,7 @@ import com.intellij.ide.fileTemplates.FileTemplateUtil import com.intellij.ide.fileTemplates.JavaTemplateUtil import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.readAction import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction @@ -52,6 +53,7 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.util.toPsiDirectory import org.jetbrains.kotlin.idea.util.ImportInsertHelperImpl +import org.jetbrains.kotlin.idea.util.projectStructure.allModules import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtNamedFunction @@ -91,6 +93,8 @@ import org.utbot.intellij.plugin.util.IntelliJApiHelper.Target.THREAD_POOL import org.utbot.intellij.plugin.util.IntelliJApiHelper.Target.WRITE_ACTION import org.utbot.intellij.plugin.util.IntelliJApiHelper.run import org.utbot.intellij.plugin.util.RunConfigurationHelper +import org.utbot.intellij.plugin.util.assertIsDispatchThread +import org.utbot.intellij.plugin.util.assertIsWriteThread import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested import org.utbot.sarif.Sarif import org.utbot.sarif.SarifReport @@ -110,9 +114,10 @@ object CodeGenerationController { model: GenerateTestsModel, classesWithTests: Map, psi2KClass: Map, - proc: EngineProcess, + proccess: EngineProcess, indicator: ProgressIndicator ) { + assertIsDispatchThread() val baseTestDirectory = model.testSourceRoot?.toPsiDirectory(model.project) ?: return val allTestPackages = getPackageDirectories(baseTestDirectory) @@ -138,7 +143,7 @@ object CodeGenerationController { val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}") runWriteCommandAction(model.project, "Generate tests with UtBot", null, { generateCodeAndReport( - proc, + proccess, testSetsId, srcClass, cut, @@ -166,23 +171,21 @@ object CodeGenerationController { run(EDT_LATER, indicator,"Go to EDT for utility class creation") { run(WRITE_ACTION, indicator, "Need write action for utility class creation") { createUtilityClassIfNeed(utilClassListener, model, baseTestDirectory, indicator) - run(EDT_LATER, indicator, "Proceed test report") { - proceedTestReport(proc, model) - run(THREAD_POOL, indicator, "Generate summary Sarif report") { - val sarifReportsPath = - model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) - UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Merge Sarif reports", 0.75) - mergeSarifReports(model, sarifReportsPath) - if (model.runGeneratedTestsWithCoverage) { - UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Start tests with coverage", 0.95) - RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers) - } - proc.forceTermination() - UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Generation finished", 1.0) + run(THREAD_POOL, indicator, "Generate summary Sarif report") { + proceedTestReport(proccess, model) + val sarifReportsPath = + model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) + UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Merge Sarif reports", 0.75) + mergeSarifReports(model, sarifReportsPath) + if (model.runGeneratedTestsWithCoverage) { + UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Start tests with coverage", 0.95) + RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers) + } + proccess.terminate() + UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Generation finished", 1.0) - run(EDT_LATER, null, "Run sarif-based inspections") { - runInspectionsIfNeeded(model, srcClassPathToSarifReport) - } + run(EDT_LATER, null, "Run sarif-based inspections") { + runInspectionsIfNeeded(model, srcClassPathToSarifReport) } } } @@ -213,7 +216,7 @@ object CodeGenerationController { .doInspections(AnalysisScope(model.project)) } - private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) { + private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) = runReadAction { try { // Parametrized tests are not supported in tests report yet // TODO JIRA:1507 @@ -654,21 +657,22 @@ object CodeGenerationController { utilClassListener: UtilClassListener, indicator: ProgressIndicator ) { + assertIsWriteThread() val classMethods = srcClass.extractClassMethodsIncludingNested(false) - val paramNames = try { - DumbService.getInstance(model.project) - .runReadActionInSmartMode(Computable { proc.findMethodParamNames(classUnderTest, classMethods) }) - } catch (e: Exception) { - logger.warn(e) { "Cannot find method param names for ${classUnderTest.name}" } - reportsCountDown.countDown() - return - } val testPackageName = testClass.packageName val editor = CodeInsightUtil.positionCursorAtLBrace(testClass.project, filePointer.containingFile, testClass) //TODO: Use PsiDocumentManager.getInstance(model.project).getDocument(file) // if we don't want to open _all_ new files with tests in editor one-by-one run(THREAD_POOL, indicator, "Rendering test code") { val (generatedTestsCode, utilClassKind) = try { + val paramNames = try { + DumbService.getInstance(model.project) + .runReadActionInSmartMode(Computable { proc.findMethodParamNames(classUnderTest, classMethods) }) + } catch (e: Exception) { + logger.warn(e) { "Cannot find method param names for ${classUnderTest.name}" } + reportsCountDown.countDown() + return@run + } proc.render( testSetsId, classUnderTest, diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt index 52fac9f788..9463f4d0fc 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt @@ -23,6 +23,7 @@ import com.intellij.util.concurrency.AppExecutorUtil import com.intellij.util.containers.nullize import com.intellij.util.io.exists import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import kotlinx.coroutines.runBlocking import mu.KotlinLogging import org.jetbrains.kotlin.idea.util.module import org.utbot.framework.UtSettings @@ -41,7 +42,6 @@ import org.utbot.intellij.plugin.ui.GenerateTestsDialogWindow import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater import org.utbot.intellij.plugin.ui.utils.testModules import org.utbot.intellij.plugin.util.* -import org.utbot.rd.terminateOnException import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -49,6 +49,7 @@ import java.util.concurrent.TimeUnit import kotlin.io.path.pathString import org.utbot.framework.plugin.api.util.LockFile import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle +import org.utbot.rd.terminateOnException object UtTestsDialogProcessor { private val logger = KotlinLogging.logger {} @@ -129,33 +130,32 @@ object UtTestsDialogProcessor { (object : Task.Backgroundable(project, "Generate tests") { override fun run(indicator: ProgressIndicator) { + assertIsNonDispatchThread() if (!LockFile.lock()) { return } try { - val ldef = LifetimeDefinition() - ldef.terminateOnException { lifetime -> - val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout) - - indicator.isIndeterminate = false - updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0) + val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout) - val buildPaths = ReadAction - .nonBlocking { findPaths(model.srcClasses) } - .executeSynchronously() - ?: return + indicator.isIndeterminate = false + updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0) - val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths + val buildPaths = ReadAction + .nonBlocking { findPaths(model.srcClasses) } + .executeSynchronously() + ?: return - val testSetsByClass = mutableMapOf() - val psi2KClass = mutableMapOf() - var processedClasses = 0 - val totalClasses = model.srcClasses.size + val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths - val proc = EngineProcess(lifetime, project) + val testSetsByClass = mutableMapOf() + val psi2KClass = mutableMapOf() + var processedClasses = 0 + val totalClasses = model.srcClasses.size + val process = EngineProcess.createBlocking(project) - proc.setupUtContext(buildDirs + classpathList) - proc.createTestGenerator( + process.terminateOnException { _ -> + process.setupUtContext(buildDirs + classpathList) + process.createTestGenerator( buildDirs, classpath, pluginJarsPath.joinToString(separator = File.pathSeparator), @@ -170,7 +170,7 @@ object UtTestsDialogProcessor { val (methods, className) = DumbService.getInstance(project) .runReadActionInSmartMode(Computable { val canonicalName = srcClass.canonicalName - val classId = proc.obtainClassId(canonicalName) + val classId = process.obtainClassId(canonicalName) psi2KClass[srcClass] = classId val srcMethods = if (model.extractMembersFromSrcClasses) { @@ -183,7 +183,7 @@ object UtTestsDialogProcessor { } else { srcClass.extractClassMethodsIncludingNested(false) } - proc.findMethodsInClassMatchingSelected(classId, srcMethods) to srcClass.name + process.findMethodsInClassMatchingSelected(classId, srcMethods) to srcClass.name }) if (methods.isEmpty()) { @@ -231,7 +231,7 @@ object UtTestsDialogProcessor { ) }, 0, 500, TimeUnit.MILLISECONDS) try { - val rdGenerateResult = proc.generate( + val rdGenerateResult = process.generate( mockFrameworkInstalled, model.staticsMocking.isConfigured, model.conflictTriggers, @@ -281,7 +281,7 @@ object UtTestsDialogProcessor { // indicator.checkCanceled() invokeLater { - generateTests(model, testSetsByClass, psi2KClass, proc, indicator) + generateTests(model, testSetsByClass, psi2KClass, process, indicator) logger.info { "Generation complete" } } } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index c4c9823cb1..ba9a01ef3f 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -10,12 +10,12 @@ import com.intellij.refactoring.util.classMembers.MemberInfo import com.jetbrains.rd.util.ConcurrentHashMap import com.jetbrains.rd.util.Logger import com.jetbrains.rd.util.lifetime.Lifetime -import com.jetbrains.rd.util.lifetime.throwIfNotAlive +import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import mu.KotlinLogging import org.utbot.common.* +import org.utbot.common.PathUtil.toPathOrNull import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.runIdeaProcessWithDebug import org.utbot.framework.codegen.domain.ForceStaticMocking @@ -27,7 +27,6 @@ import org.utbot.framework.codegen.domain.TestFramework import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.services.JdkInfo -import org.utbot.framework.plugin.services.JdkInfoDefaultProvider import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.plugin.services.WorkingDirService import org.utbot.framework.process.OpenModulesContainer @@ -36,19 +35,24 @@ import org.utbot.framework.process.generated.Signature import org.utbot.framework.util.Conflict import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.util.KryoHelper +import org.utbot.intellij.plugin.UtbotBundle import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener +import org.utbot.intellij.plugin.util.assertIsNonDispatchThread +import org.utbot.intellij.plugin.util.assertIsReadAccessAllowed import org.utbot.intellij.plugin.util.signature -import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.* import org.utbot.rd.loggers.UtRdKLoggerFactory -import org.utbot.rd.rdPortArgument -import org.utbot.rd.startUtProcessWithRdServer import org.utbot.sarif.SourceFindingStrategy import java.io.File +import java.nio.charset.Charset +import java.nio.file.Files import java.nio.file.Path -import kotlin.io.path.deleteIfExists +import java.nio.file.StandardCopyOption +import java.sql.Timestamp +import java.text.SimpleDateFormat +import java.time.LocalDateTime import kotlin.io.path.pathString -import kotlin.random.Random import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties @@ -58,201 +62,174 @@ private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdE data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long) -class EngineProcess(parent: Lifetime, val project: Project) { +class EngineProcess private constructor(val project: Project, rdProcess: ProcessWithRdServer) : + ProcessWithRdServer by rdProcess { companion object { + private val log4j2ConfigFile: File + init { + engineProcessLogDirectory.mkdirs() + engineProcessLogConfigurations.mkdirs() Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) + + val customFile = File(UtSettings.ideaProcessLogConfigFile) + + if (customFile.exists()) { + log4j2ConfigFile = customFile + } else { + log4j2ConfigFile = Files.createTempFile(engineProcessLogConfigurations.toPath(), null, ".xml").toFile() + log4j2ConfigFile.deleteOnExit() + this.javaClass.classLoader.getResourceAsStream("log4j2.xml")?.use { logConfig -> + val resultConfig = logConfig.readBytes().toString(Charset.defaultCharset()) + .replace("ref=\"IdeaAppender\"", "ref=\"EngineProcessAppender\"") + Files.copy( + resultConfig.byteInputStream(), + log4j2ConfigFile.toPath(), + StandardCopyOption.REPLACE_EXISTING + ) + } + } } - } - private val ldef = parent.createNested() - private val id = Random.nextLong() - private var count = 0 - private var configPath: Path? = null - private val sourceFindingStrategies = ConcurrentHashMap() + private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - private fun getOrCreateLogConfig(): String { - var realPath = configPath - if (realPath == null) { - engineProcessLogConfigurations.mkdirs() - configPath = File.createTempFile("epl", ".xml", engineProcessLogConfigurations).apply { - val onMatch = if (UtSettings.logConcreteExecutionErrors) "NEUTRAL" else "DENY" - writeText( - """ - - - - - - - - - - - - -""" - ) - }.toPath() - realPath = configPath - logger.info("log configuration path - ${realPath!!.pathString}") + private val debugArgument: String? + get() = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=$engineProcessDebugPort" + .takeIf { runIdeaProcessWithDebug } + + private val javaExecutablePathString: Path + get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") + + private val pluginClasspath: String + get() = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( + separator = File.pathSeparator, + prefix = "\"", + postfix = "\"" + ) + + private const val startFileName = "org.utbot.framework.process.EngineMainKt" + + private fun obtainEngineProcessCommandLine(port: Int) = buildList { + add(javaExecutablePathString.pathString) + add("-ea") + OpenModulesContainer.javaVersionSpecificArguments?.let { addAll(it) } + debugArgument?.let { add(it) } + add(log4j2ConfigSwitch) + add("-cp") + add(pluginClasspath) + add(startFileName) + add(rdPortArgument(port)) } - return realPath.pathString - } - private fun debugArgument(): String { - return "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005".takeIf { runIdeaProcessWithDebug } - ?: "" - } + fun createBlocking(project: Project): EngineProcess = runBlocking { EngineProcess(project) } - private val kryoHelper = KryoHelper(ldef) - - private suspend fun engineModel(): EngineProcessModel { - ldef.throwIfNotAlive() - return lock.withLock { - var proc = current - - if (proc == null) { - proc = startUtProcessWithRdServer(ldef) { port -> - val current = JdkInfoDefaultProvider().info - val required = JdkInfoService.jdkInfoProvider.info - val java = - JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}").toString() - val cp = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( - separator = File.pathSeparator, - prefix = "\"", - postfix = "\"" - ) - val classname = "org.utbot.framework.process.EngineMainKt" - val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments + suspend operator fun invoke(project: Project): EngineProcess = + LifetimeDefinition().terminateOnException { lifetime -> + val rdProcess = startUtProcessWithRdServer(lifetime) { port -> + val cmd = obtainEngineProcessCommandLine(port) val directory = WorkingDirService.provide().toFile() - val log4j2ConfigFile = "-Dlog4j2.configurationFile=${getOrCreateLogConfig()}" - val debugArg = debugArgument() - logger.info { "java - $java\nclasspath - $cp\nport - $port" } - val cmd = mutableListOf(java, "-ea") - if (javaVersionSpecificArguments.isNotEmpty()) { - cmd.addAll(javaVersionSpecificArguments) - } - if (debugArg.isNotEmpty()) { - cmd.add(debugArg) - } - cmd.add(log4j2ConfigFile) - cmd.add("-cp") - cmd.add(cp) - cmd.add(classname) - cmd.add(rdPortArgument(port)) - ProcessBuilder(cmd).directory(directory).apply { - if (UtSettings.logConcreteExecutionErrors) { - engineProcessLogDirectory.mkdirs() - val logFile = File(engineProcessLogDirectory, "${id}-${count++}.log") - logger.info { "logFile - ${logFile.canonicalPath}" } - redirectOutput(logFile) - redirectError(logFile) - } - }.start().apply { - logger.debug { "Engine process started with PID = ${this.getPid}" } - } - }.initModels { - engineProcessModel - rdInstrumenterAdapter - rdSourceFindingStrategy - settingsModel.settingFor.set { params -> - SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings -> - val members: Collection> = - settings.javaClass.kotlin.memberProperties - val names: List> = - members.filter { it.name == params.propertyName } - val sing: KProperty1 = names.single() - val result = sing.get(settings) - logger.trace { "request for settings ${params.key}:${params.propertyName} - $result" } - result.toString() - }) - } - }.awaitSignal() - current = proc - initSourceFindingStrategies() - } + val builder = ProcessBuilder(cmd).directory(directory) + val formatted = dateTimeFormatter.format(LocalDateTime.now()) + val logFile = File(engineProcessLogDirectory, "$formatted.log") - proc.protocol.engineProcessModel - } + builder.redirectOutput(logFile) + builder.redirectError(logFile) + + val process = builder.start() + + logger.info { "Engine process started with PID = ${process.getPid}" } + logger.info { "Engine process log file - ${logFile.canonicalPath}" } + process + } + rdProcess.awaitSignal() + + return EngineProcess(project, rdProcess) + } } - private val lock = Mutex() - private var current: ProcessWithRdServer? = null + private val engineModel: EngineProcessModel = onSchedulerBlocking { protocol.engineProcessModel } + private val instrumenterAdapterModel: RdInstrumenterAdapter = onSchedulerBlocking { protocol.rdInstrumenterAdapter } + private val sourceFindingModel: RdSourceFindingStrategy = onSchedulerBlocking { protocol.rdSourceFindingStrategy } + private val settingsModel: SettingsModel = onSchedulerBlocking { protocol.settingsModel } + + private val kryoHelper = KryoHelper(lifetime) + private val sourceFindingStrategies = ConcurrentHashMap() - fun setupUtContext(classpathForUrlsClassloader: List) = runBlocking { - engineModel().setupUtContext.startSuspending(ldef, SetupContextParams(classpathForUrlsClassloader)) + fun setupUtContext(classpathForUrlsClassloader: List) { + engineModel.setupUtContext.start(lifetime, SetupContextParams(classpathForUrlsClassloader)) } - // suppose that only 1 simultaneous test generator process can be executed in idea - // so every time test generator is created - we just overwrite previous + private fun computeSourceFileByClass(params: ComputeSourceFileByClassArguments): String = + DumbService.getInstance(project).runReadActionInSmartMode { + val scope = GlobalSearchScope.allScope(project) + val psiClass = JavaFileManager.getInstance(project).findClass(params.className, scope) + val sourceFile = psiClass?.navigationElement?.containingFile?.virtualFile?.canonicalPath + + logger.debug { "computeSourceFileByClass result: $sourceFile" } + sourceFile + } + fun createTestGenerator( buildDir: List, classPath: String?, dependencyPaths: String, jdkInfo: JdkInfo, isCancelled: (Unit) -> Boolean - ) = runBlocking { - engineModel().isCancelled.set(handler = isCancelled) - current!!.protocol.rdInstrumenterAdapter.computeSourceFileByClass.set { params -> - val result = DumbService.getInstance(project).runReadActionInSmartMode { - val scope = GlobalSearchScope.allScope(project) - val psiClass = JavaFileManager.getInstance(project) - .findClass(params.className, scope) - - psiClass?.navigationElement?.containingFile?.virtualFile?.canonicalPath - } - logger.debug("computeSourceFileByClass result: $result") - result - } - engineModel().createTestGenerator.startSuspending( - ldef, - TestGeneratorParams(buildDir.toTypedArray(), classPath, dependencyPaths, JdkInfo(jdkInfo.path.pathString, jdkInfo.version)) + ) { + engineModel.isCancelled.set(handler = isCancelled) + instrumenterAdapterModel.computeSourceFileByClass.set(handler = this::computeSourceFileByClass) + + val params = TestGeneratorParams( + buildDir.toTypedArray(), + classPath, + dependencyPaths, + JdkInfo(jdkInfo.path.pathString, jdkInfo.version) ) + engineModel.createTestGenerator.start(lifetime, params) } - fun obtainClassId(canonicalName: String): ClassId = runBlocking { - kryoHelper.readObject(engineModel().obtainClassId.startSuspending(canonicalName)) + fun obtainClassId(canonicalName: String): ClassId { + assertIsNonDispatchThread() + return kryoHelper.readObject(engineModel.obtainClassId.startBlocking(canonicalName)) } - fun findMethodsInClassMatchingSelected(clazzId: ClassId, srcMethods: List): List = - runBlocking { - val srcSignatures = srcMethods.map { it.signature() } - val rdSignatures = srcSignatures.map { - Signature(it.name, it.parameterTypes) - } - kryoHelper.readObject( - engineModel().findMethodsInClassMatchingSelected.startSuspending( - FindMethodsInClassMatchingSelectedArguments(kryoHelper.writeObject(clazzId), rdSignatures) - ).executableIds - ) - } + fun findMethodsInClassMatchingSelected(clazzId: ClassId, srcMethods: List): List { + assertIsNonDispatchThread() + assertIsReadAccessAllowed() - fun findMethodParamNames(classId: ClassId, methods: List): Map> = - runBlocking { - val bySignature = methods.associate { it.signature() to it.paramNames() } - kryoHelper.readObject( - engineModel().findMethodParamNames.startSuspending( - FindMethodParamNamesArguments( - kryoHelper.writeObject( - classId - ), kryoHelper.writeObject(bySignature) - ) - ).paramNames - ) - } + val srcSignatures = srcMethods.map { it.signature() } + val rdSignatures = srcSignatures.map { Signature(it.name, it.parameterTypes) } + val binaryClassId = kryoHelper.writeObject(clazzId) + val arguments = FindMethodsInClassMatchingSelectedArguments(binaryClassId, rdSignatures) + val result = engineModel.findMethodsInClassMatchingSelected.startBlocking(arguments) - private fun MemberInfo.paramNames(): List = - (this.member as PsiMethod).parameterList.parameters.map { - if (it.name.startsWith("\$this")) - // If member is Kotlin extension function, name of first argument isn't good for further usage, - // so we better choose name based on type of receiver. - // - // There seems no API to check whether parameter is an extension receiver by PSI - it.type.presentableText - else - it.name - } + return kryoHelper.readObject(result.executableIds) + } + + fun findMethodParamNames(classId: ClassId, methods: List): Map> { + assertIsNonDispatchThread() + assertIsReadAccessAllowed() + + val bySignature = methods.associate { it.signature() to it.paramNames() } + val arguments = FindMethodParamNamesArguments( + kryoHelper.writeObject(classId), + kryoHelper.writeObject(bySignature) + ) + val result = engineModel.findMethodParamNames.startBlocking(arguments).paramNames + + return kryoHelper.readObject(result) + } + + private fun MemberInfo.paramNames(): List = (this.member as PsiMethod).parameterList.parameters.map { + if (it.name.startsWith("\$this")) + // If member is Kotlin extension function, name of first argument isn't good for further usage, + // so we better choose name based on type of receiver. + // + // There seems no API to check whether parameter is an extension receiver by PSI + it.type.presentableText + else + it.name + } fun generate( mockInstalled: Boolean, @@ -267,26 +244,25 @@ class EngineProcess(parent: Lifetime, val project: Project) { isFuzzingEnabled: Boolean, fuzzingValue: Double, searchDirectory: String - ): RdTestGenerationResult = runBlocking { - val result = engineModel().generate.startSuspending( - ldef, - GenerateParams( - mockInstalled, - staticsMockingIsConfigured, - kryoHelper.writeObject(conflictTriggers.toMutableMap()), - kryoHelper.writeObject(methods), - mockStrategyApi.name, - kryoHelper.writeObject(chosenClassesToMockAlways), - timeout, - generationTimeout, - isSymbolicEngineEnabled, - isFuzzingEnabled, - fuzzingValue, - searchDirectory - ) + ): RdTestGenerationResult { + assertIsNonDispatchThread() + val params = GenerateParams( + mockInstalled, + staticsMockingIsConfigured, + kryoHelper.writeObject(conflictTriggers.toMutableMap()), + kryoHelper.writeObject(methods), + mockStrategyApi.name, + kryoHelper.writeObject(chosenClassesToMockAlways), + timeout, + generationTimeout, + isSymbolicEngineEnabled, + isFuzzingEnabled, + fuzzingValue, + searchDirectory ) + val result = engineModel.generate.startBlocking(params) - return@runBlocking RdTestGenerationResult(result.notEmptyCases, result.testSetsId) + return RdTestGenerationResult(result.notEmptyCases, result.testSetsId) } fun render( @@ -305,64 +281,61 @@ class EngineProcess(parent: Lifetime, val project: Project) { hangingTestSource: HangingTestsTimeout, enableTestsTimeout: Boolean, testClassPackageName: String - ): Pair = runBlocking { - val result = engineModel().render.startSuspending( - ldef, RenderParams( - testSetsId, - kryoHelper.writeObject(classUnderTest), - kryoHelper.writeObject(paramNames), - generateUtilClassFile, - testFramework.id.lowercase(), - mockFramework.name, - codegenLanguage.name, - parameterizedTestSource.name, - staticsMocking.id, - kryoHelper.writeObject(forceStaticMocking), - generateWarningsForStaticsMocking, - runtimeExceptionTestsBehaviour.name, - hangingTestSource.timeoutMs, - enableTestsTimeout, - testClassPackageName - ) - ) - result.generatedCode to result.utilClassKind?.let { + ): Pair { + assertIsNonDispatchThread() + val params = RenderParams( + testSetsId, + kryoHelper.writeObject(classUnderTest), + kryoHelper.writeObject(paramNames), + generateUtilClassFile, + testFramework.id.lowercase(), + mockFramework.name, + codegenLanguage.name, + parameterizedTestSource.name, + staticsMocking.id, + kryoHelper.writeObject(forceStaticMocking), + generateWarningsForStaticsMocking, + runtimeExceptionTestsBehaviour.name, + hangingTestSource.timeoutMs, + enableTestsTimeout, + testClassPackageName + ) + val result = engineModel.render.startBlocking(params) + val realUtilClassKind = result.utilClassKind?.let { if (UtilClassKind.RegularUtUtils(codegenLanguage).javaClass.simpleName == it) UtilClassKind.RegularUtUtils(codegenLanguage) else UtilClassKind.UtUtilsWithMockito(codegenLanguage) } - } - fun forceTermination() = runBlocking { - configPath?.deleteIfExists() - engineModel().stopProcess.start(Unit) - current?.terminate() + return result.generatedCode to realUtilClassKind } - private fun initSourceFindingStrategies() { - current!!.protocol.rdSourceFindingStrategy.let { - it.getSourceFile.set { params -> - DumbService.getInstance(project).runReadActionInSmartMode { - sourceFindingStrategies[params.testSetId]!!.getSourceFile( - params.classFqn, - params.extension - )?.canonicalPath - } - } - it.getSourceRelativePath.set { params -> - DumbService.getInstance(project).runReadActionInSmartMode { - sourceFindingStrategies[params.testSetId]!!.getSourceRelativePath( - params.classFqn, - params.extension - ) - } - } - it.testsRelativePath.set { testSetId -> - DumbService.getInstance(project).runReadActionInSmartMode { - sourceFindingStrategies[testSetId]!!.testsRelativePath - } - } + private fun getSourceFile(params: SourceStrategyMethodArgs): String? = + DumbService.getInstance(project).runReadActionInSmartMode { + sourceFindingStrategies[params.testSetId]!!.getSourceFile( + params.classFqn, + params.extension + )?.canonicalPath + } + + private fun getSourceRelativePath(params: SourceStrategyMethodArgs): String = + DumbService.getInstance(project).runReadActionInSmartMode { + sourceFindingStrategies[params.testSetId]!!.getSourceRelativePath( + params.classFqn, + params.extension + ) + } + + private fun testsRelativePath(testSetId: Long): String = + DumbService.getInstance(project).runReadActionInSmartMode { + sourceFindingStrategies[testSetId]!!.testsRelativePath } + + private fun initSourceFindingStrategies() { + sourceFindingModel.getSourceFile.set(handler = this::getSourceFile) + sourceFindingModel.getSourceRelativePath.set(handler = this::getSourceRelativePath) + sourceFindingModel.testsRelativePath.set(handler = this::testsRelativePath) } fun writeSarif( @@ -370,53 +343,62 @@ class EngineProcess(parent: Lifetime, val project: Project) { testSetsId: Long, generatedTestsCode: String, sourceFindingStrategy: SourceFindingStrategy - ): String = runBlocking { + ): String { + assertIsNonDispatchThread() + + val params = WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode) + sourceFindingStrategies[testSetsId] = sourceFindingStrategy - engineModel().writeSarifReport.startSuspending( - WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode) - ) + return engineModel.writeSarifReport.startBlocking(params) } - fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple = runBlocking { - val forceMockWarning = if (model.conflictTriggers[Conflict.ForceMockHappened] == true) { - """ - Warning: Some test cases were ignored, because no mocking framework is installed in the project.
- Better results could be achieved by installing mocking framework. - """.trimIndent() - } else null - val forceStaticMockWarnings = if (model.conflictTriggers[Conflict.ForceStaticMockHappened] == true) { - """ - Warning: Some test cases were ignored, because mockito-inline is not installed in the project.
- Better results could be achieved by configuring mockito-inline. - """.trimIndent() - } else null - val testFrameworkWarnings = if (model.conflictTriggers[Conflict.TestFrameworkConflict] == true) { - """ - Warning: There are several test frameworks in the project. - To select run configuration, please refer to the documentation depending on the project build system: - Gradle, - Maven - or Idea. - """.trimIndent() - } else null - val result = engineModel().generateTestReport.startSuspending( - GenerateTestReportArgs( - eventLogMessage, - model.testPackageName, - model.isMultiPackage, - forceMockWarning, - forceStaticMockWarnings, - testFrameworkWarnings, - model.conflictTriggers.anyTriggered - ) + fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple { + assertIsNonDispatchThread() + + // todo unforce loading + + val forceMockWarning = UtbotBundle.message( + "test.report.force.mock.warning", + TestReportUrlOpeningListener.prefix, + TestReportUrlOpeningListener.mockitoSuffix + ).takeIf { model.conflictTriggers[Conflict.ForceMockHappened] == true } + val forceStaticMockWarnings = UtbotBundle.message( + "test.report.force.static.mock.warning", + TestReportUrlOpeningListener.prefix, + TestReportUrlOpeningListener.mockitoInlineSuffix + ).takeIf { model.conflictTriggers[Conflict.ForceStaticMockHappened] == true } + val testFrameworkWarnings = UtbotBundle.message("test.report.test.framework.warning") + .takeIf { model.conflictTriggers[Conflict.TestFrameworkConflict] == true } + val params = GenerateTestReportArgs( + eventLogMessage, + model.testPackageName, + model.isMultiPackage, + forceMockWarning, + forceStaticMockWarnings, + testFrameworkWarnings, + model.conflictTriggers.anyTriggered ) + val result = engineModel.generateTestReport.startBlocking(params) - return@runBlocking Triple(result.notifyMessage, result.statistics, result.hasWarnings) + return Triple(result.notifyMessage, result.statistics, result.hasWarnings) } init { - ldef.onTermination { - forceTermination() + lifetime.onTermination { + engineModel.stopProcess.start(Unit) + } + settingsModel.settingFor.set { params -> + SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings -> + val members: Collection> = + settings.javaClass.kotlin.memberProperties + val names: List> = + members.filter { it.name == params.propertyName } + val sing: KProperty1 = names.single() + val result = sing.get(settings) + logger.trace { "request for settings ${params.key}:${params.propertyName} - $result" } + result.toString() + }) } + initSourceFindingStrategies() } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt new file mode 100644 index 0000000000..9cb1809199 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt @@ -0,0 +1,23 @@ +package org.utbot.intellij.plugin.util + +import com.intellij.openapi.application.ApplicationManager + +fun assertIsDispatchThread() { + ApplicationManager.getApplication().assertIsDispatchThread() +} + +fun assertIsWriteThread() { + ApplicationManager.getApplication().isWriteThread() +} + +fun assertIsReadAccessAllowed() { + ApplicationManager.getApplication().assertReadAccessAllowed() +} + +fun assertIsWriteAccessAllowed() { + ApplicationManager.getApplication().assertWriteAccessAllowed() +} + +fun assertIsNonDispatchThread() { + ApplicationManager.getApplication().assertIsNonDispatchThread() +} \ No newline at end of file diff --git a/utbot-intellij/src/main/resources/bundles/UtbotBundle.properties b/utbot-intellij/src/main/resources/bundles/UtbotBundle.properties new file mode 100644 index 0000000000..e1abaede2b --- /dev/null +++ b/utbot-intellij/src/main/resources/bundles/UtbotBundle.properties @@ -0,0 +1,10 @@ +# {0} - Test report url prefix, {1] - suffix +test.report.force.mock.warning=Warning: Some test cases were ignored, because no mocking framework is installed in the project.
\ +Better results could be achieved by installing mocking framework. +test.report.force.static.mock.warning=Warning: Some test cases were ignored, because mockito-inline is not installed in the project.
\ +etter results could be achieved by configuring mockito-inline. +test.report.test.framework.warning=Warning: There are several test frameworks in the project.\ +To select run configuration, please refer to the documentation depending on the project build system:\ +Gradle,\ +Maven\ +or Idea. \ No newline at end of file diff --git a/utbot-intellij/src/main/resources/log4j2.xml b/utbot-intellij/src/main/resources/log4j2.xml index 5934d95932..24b5351099 100644 --- a/utbot-intellij/src/main/resources/log4j2.xml +++ b/utbot-intellij/src/main/resources/log4j2.xml @@ -1,22 +1,28 @@ - + + + + + + + - - + + - - + + - - + + - + \ No newline at end of file diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt index 0e3790aeb7..2ec60c9086 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt @@ -14,7 +14,6 @@ import com.jetbrains.rd.util.trace import kotlinx.coroutines.CancellationException import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.trySendBlocking -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeoutOrNull import org.utbot.common.* import org.utbot.rd.generated.synchronizationModel @@ -57,7 +56,10 @@ fun findRdPort(args: Array): Int { ?: throw IllegalArgumentException("No port provided") } -class CallsSynchronizer(private val ldef: LifetimeDefinition, val timeout: Duration) { +/** + * Traces when process is idle for too much time, then terminates it. + */ +class IdleWatchdog(private val ldef: LifetimeDefinition, val timeout: Duration) { private enum class State { STARTED, ENDED @@ -69,7 +71,11 @@ class CallsSynchronizer(private val ldef: LifetimeDefinition, val timeout: Durat ldef.onTermination { synchronizer.close(CancellationException("Client terminated")) } } - fun measureExecutionForTermination(block: () -> T): T { + /** + * Execute block indicating that during this activity process should not die. + * After block ended - idle timer restarts + */ + fun wrapActive(block: () -> T): T { try { synchronizer.trySendBlocking(State.STARTED) return block() @@ -78,9 +84,13 @@ class CallsSynchronizer(private val ldef: LifetimeDefinition, val timeout: Durat } } - fun measureExecutionForTermination(call: RdCall, block: (T) -> R) { + /** + * Adds callback to RdCall with indicating that during this activity process should not die. + * After block ended - idle timer restarts + */ + fun wrapActiveCall(call: RdCall, block: (T) -> R) { call.set { it -> - measureExecutionForTermination { + wrapActive { block(it) } } @@ -116,8 +126,8 @@ class CallsSynchronizer(private val ldef: LifetimeDefinition, val timeout: Durat class ClientProtocolBuilder { private var timeout = Duration.INFINITE - suspend fun start(port: Int, parent: Lifetime? = null, bindables: Protocol.(CallsSynchronizer) -> Unit) { - UtRdCoroutineScope.current // coroutine scope initialization + suspend fun start(port: Int, parent: Lifetime? = null, block: Protocol.(IdleWatchdog) -> Unit) { + UtRdCoroutineScope.initialize() val pid = currentProcessPid.toInt() val ldef = parent?.createNested() ?: LifetimeDefinition() ldef.terminateOnException { _ -> @@ -143,12 +153,12 @@ class ClientProtocolBuilder { SocketWire.Client(ldef, rdClientProtocolScheduler, port), ldef ) - val synchronizer = CallsSynchronizer(ldef, timeout) + val synchronizer = IdleWatchdog(ldef, timeout) synchronizer.setupTimeout() rdClientProtocolScheduler.pump(ldef) { clientProtocol.synchronizationModel - clientProtocol.bindables(synchronizer) + clientProtocol.block(synchronizer) } signalChildReady(port) @@ -157,7 +167,7 @@ class ClientProtocolBuilder { val answerFromMainProcess = sync.adviseForConditionAsync(ldef) { if (it == "main") { logger.trace { "received from main" } - synchronizer.measureExecutionForTermination { + synchronizer.wrapActive { sync.fire("child") } true diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt index 31834ca95b..33ab491eec 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt @@ -79,7 +79,6 @@ interface ProcessWithRdServer : LifetimedProcess { val port: Int get() = protocol.wire.serverPort - suspend fun initModels(bindables: Protocol.() -> Unit): ProcessWithRdServer suspend fun awaitSignal(): ProcessWithRdServer } @@ -99,14 +98,6 @@ class ProcessWithRdServerImpl private constructor( } } - override suspend fun initModels(bindables: Protocol.() -> Unit): ProcessWithRdServer { - protocol.scheduler.pump(lifetime) { - protocol.bindables() - } - - return this - } - override suspend fun awaitSignal(): ProcessWithRdServer { protocol.scheduler.pump(lifetime) { protocol.synchronizationModel diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt index 27571b74de..b8505615ae 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt @@ -10,6 +10,9 @@ private val coroutineDispatcher = SingleThreadScheduler(Lifetime.Eternal, "UtCor class UtRdCoroutineScope(lifetime: Lifetime) : RdCoroutineScope(lifetime) { companion object { val current = UtRdCoroutineScope(Lifetime.Eternal) + fun initialize() { + // only to initialize load and initialize class + } } init { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt index a7f9f25d25..ad44bf621f 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt @@ -1,6 +1,7 @@ package org.utbot.rd import com.jetbrains.rd.framework.* +import com.jetbrains.rd.framework.impl.RdCall import com.jetbrains.rd.framework.util.NetUtils import com.jetbrains.rd.framework.util.synchronizeWith import com.jetbrains.rd.util.lifetime.Lifetime @@ -11,8 +12,30 @@ import com.jetbrains.rd.util.reactive.ISource import com.jetbrains.rd.util.threading.SingleThreadScheduler import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred +import kotlinx.coroutines.runBlocking -// useful when initializing something +suspend fun ProcessWithRdServer.onScheduler(block: () -> T): T { + val deffered = CompletableDeferred() + protocol.scheduler.invokeOrQueue { deffered.complete(block()) } + return deffered.await() +} + +fun ProcessWithRdServer.onSchedulerBlocking(block: () -> T): T = runBlocking { onScheduler(block) } + + +fun IRdCall.startBlocking(req: TReq): TRes { + val call = this + // We do not use RdCall.sync because it requires timeouts for execution, after which request will be stopped. + // Some requests, for example test generation, might be either really long, or have their own timeouts. + // To honor their timeout logic we do not use RdCall.sync. + return runBlocking { call.startSuspending(req) } +} + +/** + * Terminates lifetime if exception occurs. + * Useful when initializing classes, for ex. if an exception occurs while some parts already bound to lifetime, + * and you need to terminate those parts + */ inline fun LifetimeDefinition.terminateOnException(block: (Lifetime) -> T): T { try { return block(this) @@ -22,17 +45,20 @@ inline fun LifetimeDefinition.terminateOnException(block: (Lifetime) -> T): } } -// suspends until provided lifetime is terminated or coroutine cancelled +/** + * Suspend until provided lifetime terminates or coroutine cancelles + */ suspend fun Lifetime.awaitTermination() { val deferred = CompletableDeferred() this.onTermination { deferred.complete(Unit) } deferred.await() } -// function will return when block completed -// if coroutine was cancelled - CancellationException will be thrown -// if lifetime was terminated before block completed - CancellationException will be thrown -// lambda receives lifetime that indicates whether it's operation is still required +/** + * Executes block on IScheduler and suspends until completed + * @param lifetime indicates whether it's operation is still required + * @throws CancellationException if coroutine was cancelled or lifetime was terminated before block completed +*/ suspend fun IScheduler.pump(lifetime: Lifetime, block: (Lifetime) -> T): T { val ldef = lifetime.createNested() val deferred = CompletableDeferred() @@ -46,11 +72,16 @@ suspend fun IScheduler.pump(lifetime: Lifetime, block: (Lifetime) -> T): T { return deferred.await() } -// deferred will be completed if condition was met -// if condition no more needed - cancel deferred -// if lifetime was terminated before condition was met - deferred will be canceled -// if you need timeout - wrap returned deferred it in withTimeout -suspend fun ISource.adviseForConditionAsync(lifetime: Lifetime, condition: (T) -> Boolean): Deferred { +suspend fun IScheduler.pump(block: (Lifetime) -> T): T = this.pump(Lifetime.Eternal, block) + +/** + * Advises provided condition on source and returns Deferred, + * which will be completed when condition satisfied, or cancelled when provided lifetime terminated. + * If you don't need this condition no more - you can cancel deferred. + * + * N.B. in case you need timeout - wrap deferred in withTimeout coroutine builder + */ +fun ISource.adviseForConditionAsync(lifetime: Lifetime, condition: (T) -> Boolean): Deferred { val ldef = lifetime.createNested() val deferred = CompletableDeferred() @@ -65,7 +96,7 @@ suspend fun ISource.adviseForConditionAsync(lifetime: Lifetime, condition return deferred } -suspend fun ISource.adviseForConditionAsync(lifetime: Lifetime): Deferred { +fun ISource.adviseForConditionAsync(lifetime: Lifetime): Deferred { return this.adviseForConditionAsync(lifetime) { it } } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt index 6a18fe1544..648f17bf41 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt @@ -1,9 +1,7 @@ package org.utbot.rd.loggers -import com.jetbrains.rd.util.LogLevel -import com.jetbrains.rd.util.Logger -import com.jetbrains.rd.util.defaultLogFormat -import org.utbot.common.dateFormatter +import com.jetbrains.rd.util.* +import org.utbot.common.timeFormatter import java.io.PrintStream import java.time.LocalDateTime @@ -16,18 +14,17 @@ class UtRdConsoleLogger( return level >= loggersLevel } + private fun format(category: String, level: LogLevel, message: Any?, throwable: Throwable?) : String { + val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception() //to print stacktrace + return "${LocalDateTime.now().format(timeFormatter)} | ${level.toString().uppercase().padEnd(5)} | ${category.substringAfterLast('.').padEnd(25)} | ${message?.toString()?:""} ${throwableToPrint?.getThrowableText()?.let { "| $it" }?:""}" + } + override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { if (!isEnabled(level)) return - val msg = LocalDateTime.now().format(dateFormatter) + " | ${ - defaultLogFormat( - category, - level, - message, - throwable - ) - }" + val msg = format(category, level, message, throwable) + streamToWrite.println(msg) } } \ No newline at end of file diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt index c72de72d44..64c2ded59c 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt @@ -1,13 +1,9 @@ package org.utbot.rd.loggers -import com.jetbrains.rd.util.LogLevel -import com.jetbrains.rd.util.Logger -import com.jetbrains.rd.util.defaultLogFormat +import com.jetbrains.rd.util.* import mu.KLogger -import org.utbot.common.dateFormatter -import java.time.LocalDateTime -class UtRdKLogger(private val realLogger: KLogger, private val category: String): Logger { +class UtRdKLogger(private val realLogger: KLogger): Logger { override fun isEnabled(level: LogLevel): Boolean { return when (level) { LogLevel.Trace -> realLogger.isTraceEnabled @@ -19,11 +15,16 @@ class UtRdKLogger(private val realLogger: KLogger, private val category: String) } } + private fun format(level: LogLevel, message: Any?, throwable: Throwable?): String { + val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception() //to print stacktrace + return "${message?.toString() ?: ""} ${throwableToPrint?.getThrowableText()?.let { "| $it" } ?: ""}" + } + override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { if (!isEnabled(level)) return - val msg = LocalDateTime.now().format(dateFormatter) + " | ${defaultLogFormat(category, level, message, throwable)}" + val msg = format(level, message, throwable) when (level) { LogLevel.Trace -> realLogger.trace(msg) diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt index 4c1f8910e8..2d0c4ee194 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt @@ -6,6 +6,6 @@ import mu.KLogger class UtRdKLoggerFactory(private val realLogger: KLogger) : ILoggerFactory { override fun getLogger(category: String): Logger { - return UtRdKLogger(realLogger, category) + return UtRdKLogger(realLogger) } } \ No newline at end of file From a2c9b5a829f55a7319ba39e232c706cb1a31649e Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Mon, 21 Nov 2022 23:16:47 +0300 Subject: [PATCH 2/7] [rd-refactoring] Enabling suspend and port choosing by utsettings Run configurations for debugging Instant process death detection Orphan processes termination system Some text loading optimization ChildProcess -> InstrumentedProcess Rd-based UtSettings for InstrumentedProcess --- .../build-and-run-tests-from-branch.yml | 2 +- .../run-chosen-tests-from-branch.yml | 2 +- .run/Debug All.run.xml | 8 +++ .run/Debug Engine Process.run.xml | 7 +++ .run/Debug Instrumented Process.run.xml | 7 +++ .run/Listen for Engine Process.run.xml | 15 +++++ .run/Listen for Instrumented Process.run.xml | 15 +++++ .run/Run IDE.run.xml | 23 +++++++ docs/RD for UnitTestBot.md | 10 +-- docs/contributing/InterProcessDebugging.md | 6 +- .../main/kotlin/org/utbot/common/JvmUtil.kt | 5 +- .../kotlin/org/utbot/framework/UtSettings.kt | 50 ++++++++++----- .../framework/plugin/api/UtExecutionResult.kt | 4 +- .../plugin/services/JdkInfoService.kt | 2 +- .../plugin/services/WorkingDirService.kt | 2 +- .../examples/strings/StringExamplesTest.kt | 2 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 +- .../utbot/framework/codegen/domain/Domain.kt | 4 +- .../concrete/UtExecutionInstrumentation.kt | 4 +- .../framework/minimization/Minimization.kt | 2 +- .../framework/plugin/api/TestCaseGenerator.kt | 2 +- .../plugin/api/utils/DependencyUtils.kt | 2 +- .../{EngineMain.kt => EngineProcessMain.kt} | 2 + .../generated/EngineProcessModel.Generated.kt | 2 +- ...ated.kt => EngineProcessRoot.Generated.kt} | 16 ++--- .../RdInstrumenterAdapter.Generated.kt | 2 +- .../RdSourceFindingStrategy.Generated.kt | 2 +- utbot-instrumentation-tests/build.gradle | 2 +- .../examples/TestCoverageInstrumentation.kt | 6 +- .../examples/TestInvokeInstrumentation.kt | 4 +- .../kotlin/org/utbot/examples/TestIsolated.kt | 4 +- utbot-instrumentation/build.gradle | 4 +- .../utbot/instrumentation/ConcreteExecutor.kt | 50 +++++++-------- .../instrumentation/Instrumentation.kt | 2 +- .../coverage/CoverageInstrumentation.kt | 2 +- ...dProcess.kt => InstrumentedProcessMain.kt} | 19 +++--- ...Runner.kt => InstrumentedProcessRunner.kt} | 24 +++---- ...ationProcess.kt => InstrumentedProcess.kt} | 32 ++++++---- ... => InstrumentedProcessModel.Generated.kt} | 62 +++++++++---------- .../InstrumentedProcessRoot.Generated.kt | 58 +++++++++++++++++ .../util/InstrumentationException.kt | 10 +-- .../org/utbot/intellij/plugin/UtbotBundle.kt | 9 +++ .../generator/UtTestsDialogProcessor.kt | 6 +- .../intellij/plugin/process/EngineProcess.kt | 50 ++++++++------- .../main/kotlin/org/utbot/contest/Contest.kt | 2 +- .../ContestEstimatorJdkInfoProvider.kt | 2 +- utbot-rd/build.gradle | 31 +++++----- .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 4 +- .../kotlin/org/utbot/rd/LifetimedProcess.kt | 16 +++++ .../org/utbot/rd}/RdSettingsContainer.kt | 8 +-- .../InstantProcessDeathException.kt | 12 ++++ .../rd}/generated/SettingsModel.Generated.kt | 4 +- .../rd/generated/SettingsRoot.Generated.kt | 18 +++--- .../SynchronizationModel.Generated.kt | 2 +- .../SynchronizationRoot.Generated.kt | 22 +++---- .../org/utbot/rd/models/EngineProcessModel.kt | 8 +-- ...ssModel.kt => InstrumentedProcessModel.kt} | 16 ++--- .../org/utbot/rd/models/SettingsModel.kt | 4 +- .../utbot/rd/models/SynchronizationModel.kt | 4 +- 59 files changed, 450 insertions(+), 247 deletions(-) create mode 100644 .run/Debug All.run.xml create mode 100644 .run/Debug Engine Process.run.xml create mode 100644 .run/Debug Instrumented Process.run.xml create mode 100644 .run/Listen for Engine Process.run.xml create mode 100644 .run/Listen for Instrumented Process.run.xml create mode 100644 .run/Run IDE.run.xml rename utbot-framework/src/main/kotlin/org/utbot/framework/process/{EngineMain.kt => EngineProcessMain.kt} (99%) rename utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/{EngineProcessProtocolRoot.Generated.kt => EngineProcessRoot.Generated.kt} (76%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/{ChildProcess.kt => InstrumentedProcessMain.kt} (88%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/{ChildProcessRunner.kt => InstrumentedProcessRunner.kt} (82%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/{UtInstrumentationProcess.kt => InstrumentedProcess.kt} (61%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/{ChildProcessModel.Generated.kt => InstrumentedProcessModel.Generated.kt} (89%) create mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessRoot.Generated.kt rename {utbot-framework/src/main/kotlin/org/utbot/framework/process => utbot-rd/src/main/kotlin/org/utbot/rd}/RdSettingsContainer.kt (87%) create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt rename {utbot-framework/src/main/kotlin/org/utbot/framework/process => utbot-rd/src/main/kotlin/org/utbot/rd}/generated/SettingsModel.Generated.kt (98%) rename utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt => utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsRoot.Generated.kt (73%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt => utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationRoot.Generated.kt (63%) rename utbot-rd/src/main/rdgen/org/utbot/rd/models/{ChildProcessModel.kt => InstrumentedProcessModel.kt} (78%) diff --git a/.github/workflows/build-and-run-tests-from-branch.yml b/.github/workflows/build-and-run-tests-from-branch.yml index d21242d320..ff5f7e456c 100644 --- a/.github/workflows/build-and-run-tests-from-branch.yml +++ b/.github/workflows/build-and-run-tests-from-branch.yml @@ -131,7 +131,7 @@ jobs: name: utbot_temp ${{ matrix.project.PART_NAME }} path: | /tmp/UTBot/generated*/* - /tmp/UTBot/utbot-childprocess-errors/* + /tmp/UTBot/utbot-instrumentedprocess-errors/* - name: Upload test report if tests have failed if: ${{ failure() }} uses: actions/upload-artifact@v3 diff --git a/.github/workflows/run-chosen-tests-from-branch.yml b/.github/workflows/run-chosen-tests-from-branch.yml index 97264dac0d..786c55eeb7 100644 --- a/.github/workflows/run-chosen-tests-from-branch.yml +++ b/.github/workflows/run-chosen-tests-from-branch.yml @@ -67,7 +67,7 @@ jobs: name: generated-tests path: | /tmp/UTBot/generated*/* - /tmp/UTBot/utbot-childprocess-errors/* + /tmp/UTBot/utbot-instrumentedprocess-errors/* - name: Upload utbot-framework logs if: ${{ always() && github.event.inputs.project-name == 'utbot-framework' }} uses: actions/upload-artifact@v3 diff --git a/.run/Debug All.run.xml b/.run/Debug All.run.xml new file mode 100644 index 0000000000..20d77a1085 --- /dev/null +++ b/.run/Debug All.run.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.run/Debug Engine Process.run.xml b/.run/Debug Engine Process.run.xml new file mode 100644 index 0000000000..1b6d823900 --- /dev/null +++ b/.run/Debug Engine Process.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.run/Debug Instrumented Process.run.xml b/.run/Debug Instrumented Process.run.xml new file mode 100644 index 0000000000..a687624701 --- /dev/null +++ b/.run/Debug Instrumented Process.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.run/Listen for Engine Process.run.xml b/.run/Listen for Engine Process.run.xml new file mode 100644 index 0000000000..b6e8bbca90 --- /dev/null +++ b/.run/Listen for Engine Process.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.run/Listen for Instrumented Process.run.xml b/.run/Listen for Instrumented Process.run.xml new file mode 100644 index 0000000000..bf7dda5c5c --- /dev/null +++ b/.run/Listen for Instrumented Process.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.run/Run IDE.run.xml b/.run/Run IDE.run.xml new file mode 100644 index 0000000000..10d313f7d7 --- /dev/null +++ b/.run/Run IDE.run.xml @@ -0,0 +1,23 @@ + + + + + + + true + true + false + + + \ No newline at end of file diff --git a/docs/RD for UnitTestBot.md b/docs/RD for UnitTestBot.md index 7b88a2798b..15c7a24067 100644 --- a/docs/RD for UnitTestBot.md +++ b/docs/RD for UnitTestBot.md @@ -112,21 +112,21 @@ Usefull: 2. There are some usefull classes to work with processes & rd: - ```LifetimedProcess``` - binds ```Lifetime``` to process. If process dies - lifetime terminates and vice versa. You can terminate lifetime manually - this will destroy process. - ```ProcessWithRdServer``` - also starts Rd server and waits for connection. - - ```UtInstrumentationProcess``` - encapsulates logic for preparing child process for executing arbitary commands. Exposes ```protocolModel``` for communicating with child process. + - ```UtInstrumentationProcess``` - encapsulates logic for preparing instrumented process for executing arbitary commands. Exposes ```protocolModel``` for communicating with instrumented process. - ```ConcreteExecutor``` is convenient wrapper for executing commands and managing resources. 3. How child communication works: - Choosing free port - - Creating child process, passing port as argument + - Creating instrumented process, passing port as argument - Both processes create protocols and bind model - - Child process setups all callbacks + - Instrumented process setups all callbacks - Parent process cannot send messages before child creates protocol, otherwise messages will be lost. So child process needs to signal that he is ready. - - Child proces creates special file in temp dir, that is observed by parent process. + - Instrumented proces creates special file in temp dir, that is observed by parent process. - When parent process spots file - he deletes it, and then sends special message for preparing child proccess instrumentation - Only then process is ready for executing commands 4. How to write custom commands for child process - Add new ```call``` in ```ProtocolModel``` - Regenerate models - - Add callback for new ```call``` in ```ChildProcess.kt``` + - Add callback for new ```call``` in ```InstrumentedProcess.kt``` - Use ```ConcreteExecutor.withProcess``` method - ___Important___ - do not add `Rdgen` as implementation dependency, it breaks some `.jar`s as it contains `kotlin-compiler-embeddable`. 5. Logs diff --git a/docs/contributing/InterProcessDebugging.md b/docs/contributing/InterProcessDebugging.md index b3b4ca363e..f9de1f7e31 100644 --- a/docs/contributing/InterProcessDebugging.md +++ b/docs/contributing/InterProcessDebugging.md @@ -18,7 +18,11 @@ The most straightforward way to debug the Engine process is the following. 1. Open `org/utbot/framework/UtSettings.kt`. 2. Set `runIdeaProcessWithDebug` property to _true_. This enables `EngineProcess.debugArgument`. -3. Find `EngineProcess.debugArgument` at `org/utbot/intellij/plugin/process/EngineProcess` and check the parameters of the debug run: + * Alternatively you can create `~/.utbot/settings.properties` file and write following: + ``` + runIdeaProcessWithDebug=true + ``` +4. Find `EngineProcess.debugArgument` at `org/utbot/intellij/plugin/process/EngineProcess` and check the parameters of the debug run: `"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005"` diff --git a/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt index ab428b7e95..86868349ec 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/JvmUtil.kt @@ -4,7 +4,4 @@ private val javaSpecificationVersion = System.getProperty("java.specification.ve val isJvm8 = javaSpecificationVersion.equals("1.8") val isJvm9Plus = !javaSpecificationVersion.contains(".") && javaSpecificationVersion.toInt() >= 9 -fun osSpecificJavaExecutable() = if (isWindows) "javaw" else "java" - -const val engineProcessDebugPort = 5005 -const val childProcessDebugPort = 5006 \ No newline at end of file +fun osSpecificJavaExecutable() = if (isWindows) "javaw" else "java" \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 8539d900b4..14768b243f 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -20,7 +20,7 @@ private const val defaultKeyForSettingsPath = "utbot.settings.path" /** * Default concrete execution timeout (in milliseconds). */ -const val DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS = 1000L +const val DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS = 1000L object UtSettings : AbstractSettings( logger, defaultKeyForSettingsPath, defaultSettingsPath @@ -260,44 +260,64 @@ object UtSettings : AbstractSettings( /** * Timeout for specific concrete execution (in milliseconds). */ - var concreteExecutionTimeoutInChildProcess: Long by getLongProperty( - DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS + var concreteExecutionTimeoutInInstrumentedProcess: Long by getLongProperty( + DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS ) /** - * Log level for concrete executor process. + * Log level for instrumented process. */ - var childProcessLogLevel by getEnumProperty(LogLevel.Info) + var instrumentedProcessLogLevel by getEnumProperty(LogLevel.Info) /** * Path to custom log4j2 configuration file for EngineProcess. * By default utbot-intellij/src/main/resources/log4j2.xml is used. * Also default value is used if provided value is not a file. */ - var ideaProcessLogConfigFile by getStringProperty("") + var engineProcessLogConfigFile by getStringProperty("") /** * Property useful only for idea * If true - runs engine process with the ability to attach a debugger - * @see runChildProcessWithDebug + * @see runInstrumentedProcessWithDebug * @see org.utbot.intellij.plugin.process.EngineProcess */ - var runIdeaProcessWithDebug by getBooleanProperty(false) + var runEngineProcessWithDebug by getBooleanProperty(false) /** - * If true, runs the child process with the ability to attach a debugger. + * Port which will be used for debugging engine process + */ + var engineProcessDebugPort by getIntProperty(5005) + + /** + * Whether engine process should suspend until debugger attached + */ + var engineProcessDebugSuspendPolicy by getBooleanProperty(true) + + /** + * Port which will be used for debugging instrumented process + */ + var instrumentedProcessDebugPort by getIntProperty(5006) + + /** + * Whether instrumented process should suspend until debugger attached + */ + var instrumentedProcessSuspendPolicy by getBooleanProperty(true) + + /** + * If true, runs the instrumented process with the ability to attach a debugger. * - * To debug the child process, set the breakpoint in the childProcessRunner.start() line - * and in the child process's main function and run the main process. + * To debug the instrumented process, set the breakpoint in the instrumentedProcessRunner.start() line + * and in the instrumented process's main function and run the main process. * Then run the remote JVM debug configuration in IDEA. * If you see the message in console about successful connection, then * the debugger is attached successfully. - * Now you can put the breakpoints in the child process and debug + * Now you can put the breakpoints in the instrumented process and debug * both processes simultaneously. * - * @see [org.utbot.instrumentation.process.ChildProcessRunner.cmds] + * @see [org.utbot.instrumentation.process.InstrumentedProcessRunner.cmds] */ - var runChildProcessWithDebug by getBooleanProperty(false) + var runInstrumentedProcessWithDebug by getBooleanProperty(false) /** * Number of branch instructions using for clustering executions in the test minimization phase. @@ -391,7 +411,7 @@ object UtSettings : AbstractSettings( var ignoreStaticsFromTrustedLibraries by getBooleanProperty(true) /** - * Use the sandbox in the concrete executor. + * Use the sandbox in the instrumented process. * * If true (default), the sandbox will prevent potentially dangerous calls, e.g., file access, reading * or modifying the environment, calls to `Unsafe` methods etc. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index fd58b9795f..20748404fa 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -58,14 +58,14 @@ data class UtTimeoutException(override val exception: TimeoutException) : UtExec /** * Indicates failure in concrete execution. - * For now it is explicitly throwing by ConcreteExecutor in case child process death. + * For now it is explicitly throwing by ConcreteExecutor in case instrumented process death. */ class ConcreteExecutionFailureException(cause: Throwable, errorFile: File, val processStdout: List) : Exception( buildString { appendLine() appendLine("----------------------------------------") - appendLine("The child process is dead") + appendLine("The instrumented process is dead") appendLine("Cause:\n${cause.message}") appendLine("Last 1000 lines of the error log ${errorFile.absolutePath}:") appendLine("----------------------------------------") diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt index fd4d27bd9b..62ed3560c2 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt @@ -11,7 +11,7 @@ data class JdkInfo( /** * Singleton to enable abstract access to path to JDK. - * Used in [org.utbot.instrumentation.process.ChildProcessRunner]. + * Used in [org.utbot.instrumentation.process.InstrumentedProcessRunner]. * The purpose is to use the same JDK in [org.utbot.instrumentation.ConcreteExecutor] and in the test runs. * This is necessary because the engine can be run from the various starting points, like IDEA plugin, CLI, etc. */ diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt index a714fa64b4..d7ce174bdd 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt @@ -6,7 +6,7 @@ import java.nio.file.Paths /** * Singleton to enable abstract access to the working directory. * - * Used in [org.utbot.instrumentation.process.ChildProcessRunner]. + * Used in [org.utbot.instrumentation.process.InstrumentedProcessRunner]. * The purpose is to use the same working directory in [org.utbot.instrumentation.ConcreteExecutor] * and in the test runs. */ diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt index 75f58922ce..53c540dbe8 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt @@ -382,7 +382,7 @@ internal class StringExamplesTest : UtValueTestCaseChecker( { _, i, r -> i <= 0 && r.isException() }, { cs, i, r -> i > 0 && cs == null && !r.getOrThrow() }, { cs, i, r -> i > 0 && cs != null && cs.length > i && r.getOrThrow() }, - coverage = DoNotCalculate // TODO: Coverage calculation fails in the child process with Illegal Argument Exception + coverage = DoNotCalculate // TODO: Coverage calculation fails in the instrumented process with Illegal Argument Exception ) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index b4ea2a84a2..c5519721a9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -391,7 +391,7 @@ class UtBotSymbolicEngine( */ fun fuzzing(until: Long = Long.MAX_VALUE, modelProvider: (ModelProvider) -> ModelProvider = { it }) = flow { val isFuzzable = methodUnderTest.parameters.all { classId -> - classId != Method::class.java.id && // causes the child process crash at invocation + classId != Method::class.java.id && // causes the instrumented process crash at invocation classId != Class::class.java.id // causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method) } if (!isFuzzable) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt index bfc721c52c..1cb930c247 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt @@ -1,6 +1,6 @@ package org.utbot.framework.codegen.domain -import org.utbot.framework.DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS +import org.utbot.framework.DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS import org.utbot.framework.codegen.domain.builtin.mockitoClassId import org.utbot.framework.codegen.domain.builtin.ongoingStubbingClassId import org.utbot.framework.codegen.domain.models.CgClassId @@ -581,7 +581,7 @@ data class HangingTestsTimeout(val timeoutMs: Long) { constructor() : this(DEFAULT_TIMEOUT_MS) companion object { - const val DEFAULT_TIMEOUT_MS = DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_CHILD_PROCESS_MS + const val DEFAULT_TIMEOUT_MS = DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS const val MIN_TIMEOUT_MS = 100L const val MAX_TIMEOUT_MS = 1_000_000L } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index e8da79b550..75a210f776 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -52,12 +52,12 @@ import kotlin.reflect.jvm.javaMethod * @property [stateBefore] is necessary for construction of parameters of a concrete call. * @property [instrumentation] is necessary for mocking static methods and new instances. * @property [timeout] is timeout for specific concrete execution (in milliseconds). - * By default is initialized from [UtSettings.concreteExecutionTimeoutInChildProcess] + * By default is initialized from [UtSettings.concreteExecutionTimeoutInInstrumentedProcess] */ data class UtConcreteExecutionData( val stateBefore: EnvironmentModels, val instrumentation: List, - val timeout: Long = UtSettings.concreteExecutionTimeoutInChildProcess + val timeout: Long = UtSettings.concreteExecutionTimeoutInInstrumentedProcess ) class UtConcreteExecutionResult( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt index 546e0624f2..d3b2c19918 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt @@ -27,7 +27,7 @@ import org.utbot.framework.plugin.api.UtVoidModel * We have 4 different test suites: * * Regression suite * * Error suite (invocations in which implicitly thrown unchecked exceptions reached to the top) - * * Crash suite (invocations in which the child process crashed or unexpected exception in our code occurred) + * * Crash suite (invocations in which the instrumented process crashed or unexpected exception in our code occurred) * * Timeout suite * * We want to minimize tests independently in each of these suites. diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index 1acbaa4bd3..7ed73a6c51 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -224,7 +224,7 @@ open class TestCaseGenerator( } } } - ConcreteExecutor.defaultPool.close() // TODO: think on appropriate way to close child processes + ConcreteExecutor.defaultPool.close() // TODO: think on appropriate way to close instrumented processes return methods.map { method -> diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/utils/DependencyUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/utils/DependencyUtils.kt index c3c3701c21..4075602138 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/utils/DependencyUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/utils/DependencyUtils.kt @@ -13,7 +13,7 @@ private val logger = KotlinLogging.logger {} * and their versions correspond to our requirements. * * Note: [UtExecutionInstrumentation] must be in dependency path too - * as it is used by Engine in the child process in Concrete Executor. + * as it is used by Engine in the instrumented process in Concrete Executor. */ fun checkFrameworkDependencies(dependencyPaths: String?) { if (dependencyPaths.isNullOrEmpty()) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt similarity index 99% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt index c92cf99ddb..fb252c3915 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt @@ -34,7 +34,9 @@ import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.IdleWatchdog import org.utbot.rd.ClientProtocolBuilder +import org.utbot.rd.RdSettingsContainerFactory import org.utbot.rd.findRdPort +import org.utbot.rd.generated.settingsModel import org.utbot.rd.loggers.UtRdKLoggerFactory import org.utbot.sarif.RdSourceFindingStrategyFacade import org.utbot.sarif.SarifReport diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt index 8b4555029c..9a346bebd9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -64,7 +64,7 @@ class EngineProcessModel private constructor( @JvmStatic @Deprecated("Use protocol.engineProcessModel or revise the extension scope instead", ReplaceWith("protocol.engineProcessModel")) fun create(lifetime: Lifetime, protocol: IProtocol): EngineProcessModel { - EngineProcessProtocolRoot.register(protocol.serializers) + EngineProcessRoot.register(protocol.serializers) return EngineProcessModel().apply { identify(protocol.identity, RdId.Null.mix("EngineProcessModel")) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt similarity index 76% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt index 3084ac927b..0b333c3a50 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessRoot.Generated.kt @@ -17,14 +17,14 @@ import kotlin.jvm.JvmStatic /** * #### Generated from [EngineProcessModel.kt:5] */ -class EngineProcessProtocolRoot private constructor( +class EngineProcessRoot private constructor( ) : RdExtBase() { //companion companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { - EngineProcessProtocolRoot.register(serializers) + EngineProcessRoot.register(serializers) EngineProcessModel.register(serializers) RdInstrumenterAdapter.register(serializers) RdSourceFindingStrategy.register(serializers) @@ -34,11 +34,11 @@ class EngineProcessProtocolRoot private constructor( - const val serializationHash = -4532543668004925627L + const val serializationHash = 2863869932420445069L } - override val serializersOwner: ISerializersOwner get() = EngineProcessProtocolRoot - override val serializationHash: Long get() = EngineProcessProtocolRoot.serializationHash + override val serializersOwner: ISerializersOwner get() = EngineProcessRoot + override val serializationHash: Long get() = EngineProcessRoot.serializationHash //fields //methods @@ -48,12 +48,12 @@ class EngineProcessProtocolRoot private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("EngineProcessProtocolRoot (") + printer.println("EngineProcessRoot (") printer.print(")") } //deepClone - override fun deepClone(): EngineProcessProtocolRoot { - return EngineProcessProtocolRoot( + override fun deepClone(): EngineProcessRoot { + return EngineProcessRoot( ) } //contexts diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt index 005c30622c..373a99df10 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt @@ -40,7 +40,7 @@ class RdInstrumenterAdapter private constructor( @JvmStatic @Deprecated("Use protocol.rdInstrumenterAdapter or revise the extension scope instead", ReplaceWith("protocol.rdInstrumenterAdapter")) fun create(lifetime: Lifetime, protocol: IProtocol): RdInstrumenterAdapter { - EngineProcessProtocolRoot.register(protocol.serializers) + EngineProcessRoot.register(protocol.serializers) return RdInstrumenterAdapter().apply { identify(protocol.identity, RdId.Null.mix("RdInstrumenterAdapter")) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt index 86afa5f6bd..11324e77f2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt @@ -42,7 +42,7 @@ class RdSourceFindingStrategy private constructor( @JvmStatic @Deprecated("Use protocol.rdSourceFindingStrategy or revise the extension scope instead", ReplaceWith("protocol.rdSourceFindingStrategy")) fun create(lifetime: Lifetime, protocol: IProtocol): RdSourceFindingStrategy { - EngineProcessProtocolRoot.register(protocol.serializers) + EngineProcessRoot.register(protocol.serializers) return RdSourceFindingStrategy().apply { identify(protocol.identity, RdId.Null.mix("RdSourceFindingStrategy")) diff --git a/utbot-instrumentation-tests/build.gradle b/utbot-instrumentation-tests/build.gradle index 83cc69e5a8..97b8c956aa 100644 --- a/utbot-instrumentation-tests/build.gradle +++ b/utbot-instrumentation-tests/build.gradle @@ -14,7 +14,7 @@ dependencies { } processResources { - // We will extract this jar in `ChildProcessRunner` class. + // We will extract this jar in `InstrumentedProcessRunner` class. from(configurations.fetchInstrumentationJar) { into "instrumentation-lib" } diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt index 40599120de..34f9da3fd9 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt @@ -10,7 +10,7 @@ import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage -import org.utbot.instrumentation.util.ChildProcessError +import org.utbot.instrumentation.util.InstrumentedProcessError import org.utbot.instrumentation.util.StaticEnvironment import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf @@ -67,7 +67,7 @@ class TestCoverageInstrumentation { ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() - val exc = assertThrows { + val exc = assertThrows { it.execute( ExampleClass::bar, arrayOf(testObject, 1, 2, 3) @@ -90,7 +90,7 @@ class TestCoverageInstrumentation { ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() - val exc = assertThrows { + val exc = assertThrows { it.execute( ExampleClass::bar, arrayOf(testObject, 1, 2, 3) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt index 7e4bc49eb4..85ecde4416 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt @@ -7,7 +7,7 @@ import org.utbot.examples.samples.staticenvironment.StaticExampleClass import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.util.ChildProcessError +import org.utbot.instrumentation.util.InstrumentedProcessError import kotlin.reflect.full.declaredMembers import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf @@ -40,7 +40,7 @@ class TestInvokeInstrumentation { ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() - val exc = assertThrows { + val exc = assertThrows { it.execute( ExampleClass::bar, arrayOf(testObject, 1, 2, 3) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt index dca3af7c1c..500da27859 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt @@ -5,7 +5,7 @@ import org.utbot.examples.samples.ExampleClass import org.utbot.examples.samples.staticenvironment.StaticExampleClass import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.util.ChildProcessError +import org.utbot.instrumentation.util.InstrumentedProcessError import org.utbot.instrumentation.util.Isolated import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf @@ -48,7 +48,7 @@ class TestIsolated { } - val exc = assertThrows { + val exc = assertThrows { isolatedFunction(testObject, 1, 2, 3) } diff --git a/utbot-instrumentation/build.gradle b/utbot-instrumentation/build.gradle index e9b76a34bb..11fcd78314 100644 --- a/utbot-instrumentation/build.gradle +++ b/utbot-instrumentation/build.gradle @@ -24,12 +24,12 @@ jar { manifest { attributes ( - 'Main-Class': 'org.utbot.instrumentation.process.ChildProcessKt', + 'Main-Class': 'org.utbot.instrumentation.process.InstrumentedProcessMainKt', 'Premain-Class': 'org.utbot.instrumentation.agent.Agent', ) } - // we need only classes from implementation and utbot to execute child process + // we need only classes from implementation and utbot to execute instrumented process dependsOn configurations.compileClasspath from { configurations.compileClasspath diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index e911faf312..a7721461bf 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -7,7 +7,6 @@ import com.jetbrains.rd.util.lifetime.isAlive import com.jetbrains.rd.util.lifetime.throwIfNotAlive import java.io.Closeable import java.util.concurrent.atomic.AtomicLong -import kotlin.concurrent.thread import kotlin.reflect.KCallable import kotlin.reflect.KFunction import kotlin.reflect.KProperty @@ -25,19 +24,19 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.instrumentation.Instrumentation -import org.utbot.instrumentation.process.ChildProcessRunner -import org.utbot.instrumentation.rd.UtInstrumentationProcess -import org.utbot.instrumentation.rd.generated.ComputeStaticFieldParams -import org.utbot.instrumentation.rd.generated.InvokeMethodCommandParams -import org.utbot.instrumentation.util.ChildProcessError +import org.utbot.instrumentation.process.InstrumentedProcessRunner +import org.utbot.instrumentation.process.generated.ComputeStaticFieldParams +import org.utbot.instrumentation.process.generated.InvokeMethodCommandParams +import org.utbot.instrumentation.rd.InstrumentedProcess +import org.utbot.instrumentation.util.InstrumentedProcessError import org.utbot.rd.loggers.UtRdKLoggerFactory private val logger = KotlinLogging.logger {} /** - * Creates [ConcreteExecutor], which delegates `execute` calls to the child process, and applies the given [block] to it. + * Creates [ConcreteExecutor], which delegates `execute` calls to the instrumented process, and applies the given [block] to it. * - * The child process will search for the classes in [pathsToUserClasses] and will use [instrumentation] for instrumenting. + * The instrumented process will search for the classes in [pathsToUserClasses] and will use [instrumentation] for instrumenting. * * Specific instrumentation can add functionality to [ConcreteExecutor] via Kotlin extension functions. * @@ -96,12 +95,12 @@ class ConcreteExecutorPool(val maxCount: Int = Settings.defaultConcreteExecutorP } /** - * Concrete executor class. Takes [pathsToUserClasses] where the child process will search for the classes. Paths should + * Concrete executor class. Takes [pathsToUserClasses] where the instrumented process will search for the classes. Paths should * be separated with [java.io.File.pathSeparatorChar]. * * If [instrumentation] depends on other classes, they should be passed in [pathsToDependencyClasses]. * - * Also takes [instrumentation] object which will be used in the child process for the instrumentation. + * Also takes [instrumentation] object which will be used in the instrumented process for the instrumentation. * * @param TIResult the return type of [Instrumentation.invoke] function for the given [instrumentation]. */ @@ -111,7 +110,7 @@ class ConcreteExecutor> p internal val pathsToDependencyClasses: String ) : Closeable, Executor { private val ldef: LifetimeDefinition = LifetimeDefinition() - private val childProcessRunner: ChildProcessRunner = ChildProcessRunner() + private val instrumentedProcessRunner: InstrumentedProcessRunner = InstrumentedProcessRunner() companion object { @@ -126,7 +125,6 @@ class ConcreteExecutor> p init { Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) - Runtime.getRuntime().addShutdownHook(thread(start = false) { defaultPool.close() }) } /** @@ -153,18 +151,18 @@ class ConcreteExecutor> p get() = ldef.isAlive private val corMutex = Mutex() - private var processInstance: UtInstrumentationProcess? = null + private var processInstance: InstrumentedProcess? = null // this function is intended to be called under corMutex - private suspend fun regenerate(): UtInstrumentationProcess { + private suspend fun regenerate(): InstrumentedProcess { ldef.throwIfNotAlive() - var proc: UtInstrumentationProcess? = processInstance + var proc: InstrumentedProcess? = processInstance if (proc == null || !proc.lifetime.isAlive) { - proc = UtInstrumentationProcess( + proc = InstrumentedProcess( ldef, - childProcessRunner, + instrumentedProcessRunner, instrumentation, pathsToUserClasses, pathsToDependencyClasses, @@ -177,18 +175,18 @@ class ConcreteExecutor> p } /** - * Main entry point for communicating with child process. + * Main entry point for communicating with instrumented process. * Use this function every time you want to access protocol model. - * This method prepares child process for execution and ensures it is alive before giving it block + * This method prepares instrumented process for execution and ensures it is alive before giving it block * * @param exclusively if true - executes block under mutex. * This guarantees that no one can access protocol model - no other calls made before block completes */ - suspend fun withProcess(exclusively: Boolean = false, block: suspend UtInstrumentationProcess.() -> T): T { - fun throwConcreteIfDead(e: Throwable, proc: UtInstrumentationProcess?) { + suspend fun withProcess(exclusively: Boolean = false, block: suspend InstrumentedProcess.() -> T): T { + fun throwConcreteIfDead(e: Throwable, proc: InstrumentedProcess?) { if (proc?.lifetime?.isAlive != true) { throw ConcreteExecutionFailureException(e, - childProcessRunner.errorLogFile, + instrumentedProcessRunner.errorLogFile, try { proc?.run { process.inputStream.bufferedReader().lines().toList() } ?: emptyList() } catch (e: Exception) { @@ -200,7 +198,7 @@ class ConcreteExecutor> p sendTimestamp.set(System.currentTimeMillis()) - var proc: UtInstrumentationProcess? = null + var proc: InstrumentedProcess? = null try { if (exclusively) { @@ -216,7 +214,7 @@ class ConcreteExecutor> p catch (e: CancellationException) { // cancellation can be from 2 causes // 1. process died, its lifetime terminated, so operation was cancelled - // this clearly indicates child process death -> ConcreteExecutionFailureException + // this clearly indicates instrumented process death -> ConcreteExecutionFailureException throwConcreteIfDead(e, proc) // 2. it can be ordinary timeout from coroutine. then just rethrow throw e @@ -226,7 +224,7 @@ class ConcreteExecutor> p // 1. be dead because of this exception throwConcreteIfDead(e, proc) // 2. might be deliberately thrown and process still can operate - throw ChildProcessError(e) + throw InstrumentedProcessError(e) } finally { receiveTimeStamp.set(System.currentTimeMillis()) @@ -253,7 +251,7 @@ class ConcreteExecutor> p } /** - * Executes [kCallable] in the child process with the supplied [arguments] and [parameters], e.g. static environment. + * Executes [kCallable] in the instrumented process with the supplied [arguments] and [parameters], e.g. static environment. * * @return the processed result of the method call. */ diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt index 362989dbd5..b2fbe6e0a6 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt @@ -28,7 +28,7 @@ interface Instrumentation : ClassFileTransformer fun getStaticField(fieldId: FieldId): Result<*> /** - * Will be called in the very beginning in the child process. + * Will be called in the very beginning in the instrumented process. */ fun init(pathsToUserClasses: Set) {} } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt index 49d4c5026e..bba5bd2e1e 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt @@ -8,11 +8,11 @@ import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.InvokeWithStaticsInstrumentation import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter -import org.utbot.instrumentation.rd.generated.CollectCoverageParams import org.utbot.instrumentation.util.CastProbesArrayException import org.utbot.instrumentation.util.NoProbesArrayException import java.security.ProtectionDomain import org.utbot.framework.plugin.api.FieldId +import org.utbot.instrumentation.process.generated.CollectCoverageParams data class CoverageInfo( val methodToInstrRange: Map, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt similarity index 88% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt index faff4abcf1..f2d0e5ec44 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt @@ -8,14 +8,16 @@ import org.utbot.framework.plugin.api.util.UtContext import org.utbot.instrumentation.agent.Agent import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation -import org.utbot.instrumentation.rd.generated.ChildProcessModel -import org.utbot.instrumentation.rd.generated.CollectCoverageResult -import org.utbot.instrumentation.rd.generated.InvokeMethodCommandResult -import org.utbot.instrumentation.rd.generated.childProcessModel +import org.utbot.instrumentation.process.generated.CollectCoverageResult +import org.utbot.instrumentation.process.generated.InstrumentedProcessModel +import org.utbot.instrumentation.process.generated.InvokeMethodCommandResult +import org.utbot.instrumentation.process.generated.instrumentedProcessModel import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.IdleWatchdog import org.utbot.rd.ClientProtocolBuilder +import org.utbot.rd.RdSettingsContainerFactory import org.utbot.rd.findRdPort +import org.utbot.rd.generated.settingsModel import org.utbot.rd.loggers.UtRdConsoleLoggerFactory import java.io.File import java.io.OutputStream @@ -54,7 +56,7 @@ internal object HandlerClassesLoader : URLClassLoader(emptyArray()) { */ const val DISABLE_SANDBOX_OPTION = "--disable-sandbox" const val ENABLE_LOGS_OPTION = "--enable-logs" -private val logger = getLogger("ChildProcess") +private val logger = getLogger("InstrumentedProcess") private val messageFromMainTimeout: Duration = 120.seconds fun logLevelArgument(level: LogLevel): String { @@ -68,7 +70,7 @@ private fun findLogLevel(args: Array): LogLevel { } /** - * It should be compiled into separate jar file (child_process.jar) and be run with an agent (agent.jar) option. + * It should be compiled into separate jar file (instrumented_process.jar) and be run with an agent (agent.jar) option. */ fun main(args: Array) = runBlocking { // We don't want user code to litter the standard output, so we redirect it. @@ -95,7 +97,8 @@ fun main(args: Array) = runBlocking { ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) { val kryoHelper = KryoHelper(lifetime) logger.info { "setup started" } - childProcessModel.setup(kryoHelper, it) + AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel)) + instrumentedProcessModel.setup(kryoHelper, it) logger.info { "setup ended" } } } catch (e: Throwable) { @@ -110,7 +113,7 @@ private lateinit var pathsToUserClasses: Set private lateinit var pathsToDependencyClasses: Set private lateinit var instrumentation: Instrumentation<*> -private fun ChildProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog) { +private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatchdog) { watchdog.wrapActiveCall(warmup) { logger.debug { "received warmup request" } val time = measureTimeMillis { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt similarity index 82% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt index 053b9ec5e2..912d0e8c4e 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt @@ -8,18 +8,16 @@ import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.UtSettings import org.utbot.framework.plugin.services.WorkingDirService import org.utbot.framework.process.OpenModulesContainer -import org.utbot.instrumentation.Settings import org.utbot.instrumentation.agent.DynamicClassTransformer import org.utbot.rd.rdPortArgument import java.io.File import java.time.LocalDateTime -import kotlin.random.Random private val logger = KotlinLogging.logger {} -class ChildProcessRunner { +class InstrumentedProcessRunner { private val cmds: List by lazy { - val debugCmd = listOfNotNull(DEBUG_RUN_CMD.takeIf { UtSettings.runChildProcessWithDebug }) + val debugCmd = listOfNotNull(DEBUG_RUN_CMD.takeIf { UtSettings.runInstrumentedProcessWithDebug }) val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments ?: emptyList() val pathToJava = JdkInfoService.provide().path @@ -34,7 +32,7 @@ class ChildProcessRunner { fun start(rdPort: Int): Process { val portArgument = rdPortArgument(rdPort) - logger.debug { "Starting child process: ${cmds.joinToString(" ")} $portArgument" } + logger.debug { "Starting instrumented process: ${cmds.joinToString(" ")} $portArgument" } UT_BOT_TEMP_DIR.mkdirs() val formatted = dateTimeFormatter.format(LocalDateTime.now()) @@ -46,7 +44,7 @@ class ChildProcessRunner { if (!UtSettings.useSandbox) { add(DISABLE_SANDBOX_OPTION) } - add(logLevelArgument(UtSettings.childProcessLogLevel)) + add(logLevelArgument(UtSettings.instrumentedProcessLogLevel)) add(portArgument) } @@ -55,17 +53,19 @@ class ChildProcessRunner { .directory(directory) return processBuilder.start().also { - logger.info { "Child process started with PID=${it.getPid}" } - logger.info { "Child process log file: ${errorLogFile.absolutePath}" } + logger.info { "Instrumented process started with PID=${it.getPid}" } + logger.info { "Instrumented process log file: ${errorLogFile.absolutePath}" } } } companion object { + private fun suspendValue(): String = if (UtSettings.engineProcessDebugSuspendPolicy) "y" else "n" + private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation" - private const val ERRORS_FILE_PREFIX = "utbot-childprocess-errors" + private const val ERRORS_FILE_PREFIX = "utbot-instrumentedprocess-errors" private const val INSTRUMENTATION_LIB = "lib" - private const val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=$childProcessDebugPort" + private val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendValue()},quiet=y,address=${UtSettings.instrumentedProcessDebugPort}" private val UT_BOT_TEMP_DIR: File = File(utBotTempDirectory.toFile(), ERRORS_FILE_PREFIX) @@ -92,7 +92,7 @@ class ChildProcessRunner { val unzippedJarName = "$UTBOT_INSTRUMENTATION-${currentProcessPid}.jar" val instrumentationJarFile = File(tempDir, unzippedJarName) - ChildProcessRunner::class.java.classLoader + InstrumentedProcessRunner::class.java.classLoader .firstOrNullResourceIS(INSTRUMENTATION_LIB) { resourcePath -> resourcePath.contains(UTBOT_INSTRUMENTATION) && resourcePath.endsWith(".jar") } @@ -103,7 +103,7 @@ class ChildProcessRunner { instrumentationJarFile } ?: run { logger.debug("Failed to find jar in the resources. Trying to find it in the classpath.") - ChildProcessRunner::class.java.classLoader + InstrumentedProcessRunner::class.java.classLoader .scanForResourcesContaining(DynamicClassTransformer::class.java.nameOfPackage) .firstOrNull { it.absolutePath.contains(UTBOT_INSTRUMENTATION) && it.extension == "jar" diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt similarity index 61% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt index f9c99e8178..68571bf180 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/UtInstrumentationProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt @@ -2,52 +2,60 @@ package org.utbot.instrumentation.rd import com.jetbrains.rd.util.lifetime.Lifetime import mu.KotlinLogging +import org.utbot.framework.UtSettings import org.utbot.instrumentation.instrumentation.Instrumentation -import org.utbot.instrumentation.process.ChildProcessRunner -import org.utbot.instrumentation.rd.generated.AddPathsParams -import org.utbot.instrumentation.rd.generated.ChildProcessModel -import org.utbot.instrumentation.rd.generated.SetInstrumentationParams -import org.utbot.instrumentation.rd.generated.childProcessModel +import org.utbot.instrumentation.process.InstrumentedProcessRunner +import org.utbot.instrumentation.process.generated.AddPathsParams +import org.utbot.instrumentation.process.generated.InstrumentedProcessModel +import org.utbot.instrumentation.process.generated.SetInstrumentationParams +import org.utbot.instrumentation.process.generated.instrumentedProcessModel import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.exceptions.InstantProcessDeathException import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException private val logger = KotlinLogging.logger {} +class InstrumentedProcessInstantDeathException: InstantProcessDeathException(UtSettings.instrumentedProcessDebugPort, UtSettings.runInstrumentedProcessWithDebug) + /** * Main goals of this class: - * 1. prepare started child process for execution - initializing rd, sending paths and instrumentation + * 1. prepare started instrumented process for execution - initializing rd, sending paths and instrumentation * 2. expose bound model */ -class UtInstrumentationProcess private constructor( +class InstrumentedProcess private constructor( private val classLoader: ClassLoader?, private val rdProcess: ProcessWithRdServer ) : ProcessWithRdServer by rdProcess { val kryoHelper = KryoHelper(lifetime.createNested()).apply { classLoader?.let { setKryoClassLoader(it) } } - val chidlProcessModel: ChildProcessModel = onSchedulerBlocking { protocol.childProcessModel } + val chidlProcessModel: InstrumentedProcessModel = onSchedulerBlocking { protocol.instrumentedProcessModel } companion object { suspend operator fun > invoke( parent: Lifetime, - childProcessRunner: ChildProcessRunner, + instrumentedProcessRunner: InstrumentedProcessRunner, instrumentation: TInstrumentation, pathsToUserClasses: String, pathsToDependencyClasses: String, classLoader: ClassLoader? - ): UtInstrumentationProcess = parent.createNested().terminateOnException { lifetime -> + ): InstrumentedProcess = parent.createNested().terminateOnException { lifetime -> val rdProcess: ProcessWithRdServer = startUtProcessWithRdServer( lifetime = lifetime ) { - childProcessRunner.start(it) + val process = instrumentedProcessRunner.start(it) + if (!process.isAlive) { + throw InstrumentedProcessInstantDeathException() + } + process }.awaitSignal() logger.trace("rd process started") - val proc = UtInstrumentationProcess( + val proc = InstrumentedProcess( classLoader, rdProcess ) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt similarity index 89% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt index ae552c67a5..087da0cdb4 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.instrumentation.rd.generated +package org.utbot.instrumentation.process.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* @@ -15,9 +15,9 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [ChildProcessModel.kt:7] + * #### Generated from [InstrumentedProcessModel.kt:7] */ -class ChildProcessModel private constructor( +class InstrumentedProcessModel private constructor( private val _addPaths: RdCall, private val _warmup: RdCall, private val _setInstrumentation: RdCall, @@ -45,33 +45,33 @@ class ChildProcessModel private constructor( @JvmStatic @JvmName("internalCreateModel") @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) - internal fun createModel(lifetime: Lifetime, protocol: IProtocol): ChildProcessModel { + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): InstrumentedProcessModel { @Suppress("DEPRECATION") return create(lifetime, protocol) } @JvmStatic - @Deprecated("Use protocol.childProcessModel or revise the extension scope instead", ReplaceWith("protocol.childProcessModel")) - fun create(lifetime: Lifetime, protocol: IProtocol): ChildProcessModel { - ChildProcessProtocolRoot.register(protocol.serializers) + @Deprecated("Use protocol.instrumentedProcessModel or revise the extension scope instead", ReplaceWith("protocol.instrumentedProcessModel")) + fun create(lifetime: Lifetime, protocol: IProtocol): InstrumentedProcessModel { + InstrumentedProcessRoot.register(protocol.serializers) - return ChildProcessModel().apply { - identify(protocol.identity, RdId.Null.mix("ChildProcessModel")) - bind(lifetime, protocol, "ChildProcessModel") + return InstrumentedProcessModel().apply { + identify(protocol.identity, RdId.Null.mix("InstrumentedProcessModel")) + bind(lifetime, protocol, "InstrumentedProcessModel") } } - const val serializationHash = 3283744426733090208L + const val serializationHash = -3546055831649640704L } - override val serializersOwner: ISerializersOwner get() = ChildProcessModel - override val serializationHash: Long get() = ChildProcessModel.serializationHash + override val serializersOwner: ISerializersOwner get() = InstrumentedProcessModel + override val serializationHash: Long get() = InstrumentedProcessModel.serializationHash //fields /** - * The main process tells where the child process should search for the classes + * The main process tells where the instrumented process should search for the classes */ val addPaths: RdCall get() = _addPaths @@ -81,30 +81,30 @@ class ChildProcessModel private constructor( val warmup: RdCall get() = _warmup /** - * The main process sends [instrumentation] to the child process + * The main process sends [instrumentation] to the instrumented process */ val setInstrumentation: RdCall get() = _setInstrumentation /** - * The main process requests the child process to execute a method with the given [signature], + * The main process requests the instrumented process to execute a method with the given [signature], which declaring class's name is [className]. @property parameters are the parameters needed for an execution, e.g. static environment */ val invokeMethodCommand: RdCall get() = _invokeMethodCommand /** - * This command tells the child process to stop + * This command tells the instrumented process to stop */ val stopProcess: RdCall get() = _stopProcess /** - * This command is sent to the child process from the [ConcreteExecutor] if user wants to collect coverage for the + * This command is sent to the instrumented process from the [ConcreteExecutor] if user wants to collect coverage for the [clazz] */ val collectCoverage: RdCall get() = _collectCoverage /** - * This command is sent to the child process from the [ConcreteExecutor] if user wants to get value of static field + * This command is sent to the instrumented process from the [ConcreteExecutor] if user wants to get value of static field [fieldId] */ val computeStaticField: RdCall get() = _computeStaticField @@ -146,7 +146,7 @@ class ChildProcessModel private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("ChildProcessModel (") + printer.println("InstrumentedProcessModel (") printer.indent { print("addPaths = "); _addPaths.print(printer); println() print("warmup = "); _warmup.print(printer); println() @@ -159,8 +159,8 @@ class ChildProcessModel private constructor( printer.print(")") } //deepClone - override fun deepClone(): ChildProcessModel { - return ChildProcessModel( + override fun deepClone(): InstrumentedProcessModel { + return InstrumentedProcessModel( _addPaths.deepClonePolymorphic(), _warmup.deepClonePolymorphic(), _setInstrumentation.deepClonePolymorphic(), @@ -172,12 +172,12 @@ class ChildProcessModel private constructor( } //contexts } -val IProtocol.childProcessModel get() = getOrCreateExtension(ChildProcessModel::class) { @Suppress("DEPRECATION") ChildProcessModel.create(lifetime, this) } +val IProtocol.instrumentedProcessModel get() = getOrCreateExtension(InstrumentedProcessModel::class) { @Suppress("DEPRECATION") InstrumentedProcessModel.create(lifetime, this) } /** - * #### Generated from [ChildProcessModel.kt:8] + * #### Generated from [InstrumentedProcessModel.kt:8] */ data class AddPathsParams ( val pathsToUserClasses: String, @@ -240,7 +240,7 @@ data class AddPathsParams ( /** - * #### Generated from [ChildProcessModel.kt:28] + * #### Generated from [InstrumentedProcessModel.kt:28] */ data class CollectCoverageParams ( val clazz: ByteArray @@ -297,7 +297,7 @@ data class CollectCoverageParams ( /** - * #### Generated from [ChildProcessModel.kt:32] + * #### Generated from [InstrumentedProcessModel.kt:32] */ data class CollectCoverageResult ( val coverageInfo: ByteArray @@ -354,7 +354,7 @@ data class CollectCoverageResult ( /** - * #### Generated from [ChildProcessModel.kt:36] + * #### Generated from [InstrumentedProcessModel.kt:36] */ data class ComputeStaticFieldParams ( val fieldId: ByteArray @@ -411,7 +411,7 @@ data class ComputeStaticFieldParams ( /** - * #### Generated from [ChildProcessModel.kt:40] + * #### Generated from [InstrumentedProcessModel.kt:40] */ data class ComputeStaticFieldResult ( val result: ByteArray @@ -468,7 +468,7 @@ data class ComputeStaticFieldResult ( /** - * #### Generated from [ChildProcessModel.kt:17] + * #### Generated from [InstrumentedProcessModel.kt:17] */ data class InvokeMethodCommandParams ( val classname: String, @@ -543,7 +543,7 @@ data class InvokeMethodCommandParams ( /** - * #### Generated from [ChildProcessModel.kt:24] + * #### Generated from [InstrumentedProcessModel.kt:24] */ data class InvokeMethodCommandResult ( val result: ByteArray @@ -600,7 +600,7 @@ data class InvokeMethodCommandResult ( /** - * #### Generated from [ChildProcessModel.kt:13] + * #### Generated from [InstrumentedProcessModel.kt:13] */ data class SetInstrumentationParams ( val instrumentation: ByteArray diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessRoot.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessRoot.Generated.kt new file mode 100644 index 0000000000..62b702a472 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessRoot.Generated.kt @@ -0,0 +1,58 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") +package org.utbot.instrumentation.process.generated + +import com.jetbrains.rd.framework.* +import com.jetbrains.rd.framework.base.* +import com.jetbrains.rd.framework.impl.* + +import com.jetbrains.rd.util.lifetime.* +import com.jetbrains.rd.util.reactive.* +import com.jetbrains.rd.util.string.* +import com.jetbrains.rd.util.* +import kotlin.reflect.KClass +import kotlin.jvm.JvmStatic + + + +/** + * #### Generated from [InstrumentedProcessModel.kt:5] + */ +class InstrumentedProcessRoot private constructor( +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + InstrumentedProcessRoot.register(serializers) + InstrumentedProcessModel.register(serializers) + } + + + + + + const val serializationHash = -7874463878801458679L + + } + override val serializersOwner: ISerializersOwner get() = InstrumentedProcessRoot + override val serializationHash: Long get() = InstrumentedProcessRoot.serializationHash + + //fields + //methods + //initializer + //secondary constructor + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("InstrumentedProcessRoot (") + printer.print(")") + } + //deepClone + override fun deepClone(): InstrumentedProcessRoot { + return InstrumentedProcessRoot( + ) + } + //contexts +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/InstrumentationException.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/InstrumentationException.kt index 8935985223..09a6be548f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/InstrumentationException.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/InstrumentationException.kt @@ -26,9 +26,9 @@ class WritingToKryoException(e: Throwable) : /** * this exception is thrown only in main process. - * currently it means that {e: Throwable} happened in child process, - * but child process still can operate and not dead. - * on child process death - ConcreteExecutionFailureException is thrown + * currently it means that {e: Throwable} happened in instrumented process, + * but instrumented process still can operate and not dead. + * on instrumented process death - ConcreteExecutionFailureException is thrown */ -class ChildProcessError(e: Throwable) : - InstrumentationException("Error in the child process |> ${e.stackTraceToString()}", e) \ No newline at end of file +class InstrumentedProcessError(e: Throwable) : + InstrumentationException("Error in the instrumented process |> ${e.stackTraceToString()}", e) \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt index 8070573aa2..a13bd6424c 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/UtbotBundle.kt @@ -16,5 +16,14 @@ class UtbotBundle : DynamicBundle(BUNDLE) { ): String { return INSTANCE.getMessage(key, *params) } + + fun takeIf( + @PropertyKey(resourceBundle = BUNDLE) key: String, + vararg params: Any, + condition: () -> Boolean): String? { + if (condition()) + return message(key, params) + return null + } } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt index 9463f4d0fc..dccff24d37 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt @@ -94,7 +94,7 @@ object UtTestsDialogProcessor { val testModules = srcModule.testModules(project) JdkInfoService.jdkInfoProvider = PluginJdkInfoProvider(project) - // we want to start the child process in the same directory as the test runner + // we want to start the instrumented process in the same directory as the test runner WorkingDirService.workingDirProvider = PluginWorkingDirProvider(project) val model = GenerateTestsModel( @@ -199,7 +199,7 @@ object UtTestsDialogProcessor { ) // set timeout for concrete execution and for generated tests - UtSettings.concreteExecutionTimeoutInChildProcess = + UtSettings.concreteExecutionTimeoutInInstrumentedProcess = model.hangingTestsTimeout.timeoutMs UtSettings.useCustomJavaDocTags = @@ -355,6 +355,6 @@ object UtTestsDialogProcessor { val classpath: String, val classpathList: List, val pluginJarsPath: List - // ^ TODO: Now we collect ALL dependent libs and pass them to the child process. Most of them are redundant. + // ^ TODO: Now we collect ALL dependent libs and pass them to the instrumented process. Most of them are redundant. ) } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index ba9a01ef3f..3484d23d0e 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -11,19 +11,11 @@ import com.jetbrains.rd.util.ConcurrentHashMap import com.jetbrains.rd.util.Logger import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.LifetimeDefinition -import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking import mu.KotlinLogging import org.utbot.common.* -import org.utbot.common.PathUtil.toPathOrNull import org.utbot.framework.UtSettings -import org.utbot.framework.UtSettings.runIdeaProcessWithDebug -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour -import org.utbot.framework.codegen.domain.StaticsMocking -import org.utbot.framework.codegen.domain.TestFramework +import org.utbot.framework.codegen.domain.* import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.services.JdkInfo @@ -42,6 +34,10 @@ import org.utbot.intellij.plugin.util.assertIsNonDispatchThread import org.utbot.intellij.plugin.util.assertIsReadAccessAllowed import org.utbot.intellij.plugin.util.signature import org.utbot.rd.* +import org.utbot.rd.exceptions.InstantProcessDeathException +import org.utbot.rd.generated.SettingForResult +import org.utbot.rd.generated.SettingsModel +import org.utbot.rd.generated.settingsModel import org.utbot.rd.loggers.UtRdKLoggerFactory import org.utbot.sarif.SourceFindingStrategy import java.io.File @@ -49,8 +45,6 @@ import java.nio.charset.Charset import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardCopyOption -import java.sql.Timestamp -import java.text.SimpleDateFormat import java.time.LocalDateTime import kotlin.io.path.pathString import kotlin.reflect.KProperty1 @@ -62,6 +56,9 @@ private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdE data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long) +class EngineProcessInstantDeathException : + InstantProcessDeathException(UtSettings.engineProcessDebugPort, UtSettings.runEngineProcessWithDebug) + class EngineProcess private constructor(val project: Project, rdProcess: ProcessWithRdServer) : ProcessWithRdServer by rdProcess { companion object { @@ -72,7 +69,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process engineProcessLogConfigurations.mkdirs() Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) - val customFile = File(UtSettings.ideaProcessLogConfigFile) + val customFile = File(UtSettings.engineProcessLogConfigFile) if (customFile.exists()) { log4j2ConfigFile = customFile @@ -93,9 +90,11 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" + private fun suspendValue(): String = if (UtSettings.engineProcessDebugSuspendPolicy) "y" else "n" + private val debugArgument: String? - get() = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=$engineProcessDebugPort" - .takeIf { runIdeaProcessWithDebug } + get() = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" + .takeIf { UtSettings.runEngineProcessWithDebug } private val javaExecutablePathString: Path get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") @@ -107,9 +106,9 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process postfix = "\"" ) - private const val startFileName = "org.utbot.framework.process.EngineMainKt" + private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt" - private fun obtainEngineProcessCommandLine(port: Int) = buildList { + private fun obtainEngineProcessCommandLine(port: Int) = buildList { add(javaExecutablePathString.pathString) add("-ea") OpenModulesContainer.javaVersionSpecificArguments?.let { addAll(it) } @@ -139,6 +138,11 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process logger.info { "Engine process started with PID = ${process.getPid}" } logger.info { "Engine process log file - ${logFile.canonicalPath}" } + + if (!process.isAlive) { + throw EngineProcessInstantDeathException() + } + process } rdProcess.awaitSignal() @@ -355,20 +359,18 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple { assertIsNonDispatchThread() - // todo unforce loading - - val forceMockWarning = UtbotBundle.message( + val forceMockWarning = UtbotBundle.takeIf( "test.report.force.mock.warning", TestReportUrlOpeningListener.prefix, TestReportUrlOpeningListener.mockitoSuffix - ).takeIf { model.conflictTriggers[Conflict.ForceMockHappened] == true } - val forceStaticMockWarnings = UtbotBundle.message( + ) { model.conflictTriggers[Conflict.ForceMockHappened] == true } + val forceStaticMockWarnings = UtbotBundle.takeIf( "test.report.force.static.mock.warning", TestReportUrlOpeningListener.prefix, TestReportUrlOpeningListener.mockitoInlineSuffix - ).takeIf { model.conflictTriggers[Conflict.ForceStaticMockHappened] == true } - val testFrameworkWarnings = UtbotBundle.message("test.report.test.framework.warning") - .takeIf { model.conflictTriggers[Conflict.TestFrameworkConflict] == true } + ) { model.conflictTriggers[Conflict.ForceStaticMockHappened] == true } + val testFrameworkWarnings = + UtbotBundle.takeIf("test.report.test.framework.warning") { model.conflictTriggers[Conflict.TestFrameworkConflict] == true } val params = GenerateTestReportArgs( eventLogMessage, model.testPackageName, diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt index 01541d8394..2c2b76fc63 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt @@ -355,7 +355,7 @@ fun runGeneration( //hack if (statsForMethod.isSuspicious && (ConcreteExecutor.lastSendTimeMs - ConcreteExecutor.lastReceiveTimeMs) > 5000) { - logger.error { "HEURISTICS: close child process, because it haven't responded for long time: ${ConcreteExecutor.lastSendTimeMs - ConcreteExecutor.lastReceiveTimeMs}" } + logger.error { "HEURISTICS: close instrumented process, because it haven't responded for long time: ${ConcreteExecutor.lastSendTimeMs - ConcreteExecutor.lastReceiveTimeMs}" } ConcreteExecutor.defaultPool.close() } } diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimatorJdkInfoProvider.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimatorJdkInfoProvider.kt index 0c3ea1ed2a..0e752d96b4 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimatorJdkInfoProvider.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimatorJdkInfoProvider.kt @@ -9,7 +9,7 @@ import java.io.InputStreamReader /** * This class is used to provide a path to jdk and its version in [ContestEstimator] - * into the child process for concrete execution. + * into the instrumented process for concrete execution. */ class ContestEstimatorJdkInfoProvider(private val path: String) : JdkInfoDefaultProvider() { override val info: JdkInfo diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index ea439435b7..011575dc12 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -99,7 +99,7 @@ test { systemProperty("PROCESS_WITH_RD_SERVER_MOCK", processWithRdServerMockJar.archiveFile.get().getAsFile().canonicalPath) } -task generateChildProcessProtocolModels(type: RdGenTask) { +task generateInstrumentedProcessModels(type: RdGenTask) { def currentProjectDir = project.projectDir def instrumentationProjectDir = project.rootProject.childProjects["utbot-instrumentation"].projectDir def generatedOutputDir = new File(instrumentationProjectDir, "src/main/kotlin/org/utbot/instrumentation/rd/generated") @@ -117,14 +117,14 @@ task generateChildProcessProtocolModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.ChildProcessProtocolRoot" + root = "org.utbot.rd.models.InstrumentedProcessRoot" directory = generatedOutputDir.canonicalPath - namespace = "org.utbot.instrumentation.rd.generated" + namespace = "org.utbot.instrumentation.process.generated" } } -task generateEngineProcessProtocolModels(type: RdGenTask) { +task generateEngineProcessModels(type: RdGenTask) { def currentProjectDir = project.projectDir def ideaPluginProjectDir = project.rootProject.childProjects["utbot-framework"].projectDir def generatedOutputDir = new File(ideaPluginProjectDir, "src/main/kotlin/org/utbot/framework/process/generated") @@ -142,22 +142,14 @@ task generateEngineProcessProtocolModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.EngineProcessProtocolRoot" - - directory = generatedOutputDir.canonicalPath - namespace = "org.utbot.framework.process.generated" - } - rdParams.generator { - language = "kotlin" - transform = "symmetric" - root = "org.utbot.rd.models.SettingsProtocolRoot" + root = "org.utbot.rd.models.EngineProcessRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.framework.process.generated" } } -task generateSynchronizationModels(type: RdGenTask) { +task generateCommonModels(type: RdGenTask) { def currentProjectDir = project.projectDir def ideaPluginProjectDir = project.rootProject.childProjects["utbot-rd"].projectDir def generatedOutputDir = new File(ideaPluginProjectDir, "src/main/kotlin/org/utbot/rd/generated") @@ -175,7 +167,16 @@ task generateSynchronizationModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.SynchronizationModelRoot" + root = "org.utbot.rd.models.SynchronizationRoot" + + directory = generatedOutputDir.canonicalPath + namespace = "org.utbot.rd.generated" + } + + rdParams.generator { + language = "kotlin" + transform = "symmetric" + root = "org.utbot.rd.models.SettingsRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.rd.generated" diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt index 2ec60c9086..17199d5e13 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt @@ -30,7 +30,7 @@ internal fun childCreatedFileName(port: Int): String { return "$port.created" } -internal fun signalChildReady(port: Int) { +internal fun signalInstrumentedReady(port: Int) { processSyncDirectory.mkdirs() val signalFile = File(processSyncDirectory, childCreatedFileName(port)) @@ -161,7 +161,7 @@ class ClientProtocolBuilder { clientProtocol.block(synchronizer) } - signalChildReady(port) + signalInstrumentedReady(port) logger.info { "signalled" } clientProtocol.synchronizationModel.synchronizationSignal.let { sync -> val answerFromMainProcess = sync.adviseForConditionAsync(ldef) { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt index fb1241a617..9d0b38b94d 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt @@ -5,6 +5,7 @@ import com.jetbrains.rd.util.lifetime.LifetimeDefinition import com.jetbrains.rd.util.lifetime.throwIfNotAlive import kotlinx.coroutines.delay import java.util.concurrent.TimeUnit +import kotlin.concurrent.thread /** * Creates LifetimedProcess. @@ -66,11 +67,14 @@ class LifetimedProcessIml(override val process: Process, lifetime: Lifetime? = n init { ldef = lifetime?.createNested() ?: LifetimeDefinition() + allProcessList.add(this) ldef.onTermination { process.destroy() if (!process.waitFor(processKillTimeoutMillis, TimeUnit.MILLISECONDS)) process.destroyForcibly() + + allProcessList.remove(this) } UtRdCoroutineScope.current.launch(ldef) { while (process.isAlive) { @@ -84,4 +88,16 @@ class LifetimedProcessIml(override val process: Process, lifetime: Lifetime? = n override fun terminate() { ldef.terminate() } + + companion object { + private val allProcessList = mutableListOf() + + init { + Runtime.getRuntime().addShutdownHook(thread(start = false) { + for (proc in allProcessList.reversed()) { + proc.terminate() + } + }) + } + } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt similarity index 87% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt index 11d3a4ac62..ee881c0853 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt @@ -1,12 +1,10 @@ -package org.utbot.framework.process +package org.utbot.rd -import kotlinx.coroutines.runBlocking import mu.KLogger import org.utbot.common.SettingsContainer import org.utbot.common.SettingsContainerFactory -import org.utbot.framework.process.generated.SettingForArgument -import org.utbot.framework.process.generated.SettingsModel -import org.utbot.rd.startBlocking +import org.utbot.rd.generated.SettingForArgument +import org.utbot.rd.generated.SettingsModel import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt new file mode 100644 index 0000000000..f5e3c9b22b --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt @@ -0,0 +1,12 @@ +package org.utbot.rd.exceptions + +abstract class InstantProcessDeathException(private val debugPort: Int, private val isProcessDebug: Boolean) : Exception() { + override val message: String? + get() { + var text = "Process died before any request was executed, check process log file." + if (isProcessDebug) { + text += " Process requested debug on port - $debugPort, check if port is open." + } + return text + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt similarity index 98% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt index 76a3814baa..752be37c33 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.framework.process.generated +package org.utbot.rd.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* @@ -41,7 +41,7 @@ class SettingsModel private constructor( @JvmStatic @Deprecated("Use protocol.settingsModel or revise the extension scope instead", ReplaceWith("protocol.settingsModel")) fun create(lifetime: Lifetime, protocol: IProtocol): SettingsModel { - SettingsProtocolRoot.register(protocol.serializers) + SettingsRoot.register(protocol.serializers) return SettingsModel().apply { identify(protocol.identity, RdId.Null.mix("SettingsModel")) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsRoot.Generated.kt similarity index 73% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsRoot.Generated.kt index 62e91e16de..39557825f7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsRoot.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.framework.process.generated +package org.utbot.rd.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* @@ -17,14 +17,14 @@ import kotlin.jvm.JvmStatic /** * #### Generated from [SettingsModel.kt:5] */ -class SettingsProtocolRoot private constructor( +class SettingsRoot private constructor( ) : RdExtBase() { //companion companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { - SettingsProtocolRoot.register(serializers) + SettingsRoot.register(serializers) SettingsModel.register(serializers) } @@ -32,11 +32,11 @@ class SettingsProtocolRoot private constructor( - const val serializationHash = 6206621683627449183L + const val serializationHash = -414203168893339225L } - override val serializersOwner: ISerializersOwner get() = SettingsProtocolRoot - override val serializationHash: Long get() = SettingsProtocolRoot.serializationHash + override val serializersOwner: ISerializersOwner get() = SettingsRoot + override val serializationHash: Long get() = SettingsRoot.serializationHash //fields //methods @@ -46,12 +46,12 @@ class SettingsProtocolRoot private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("SettingsProtocolRoot (") + printer.println("SettingsRoot (") printer.print(")") } //deepClone - override fun deepClone(): SettingsProtocolRoot { - return SettingsProtocolRoot( + override fun deepClone(): SettingsRoot { + return SettingsRoot( ) } //contexts diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt index dafb2c3887..ed8c4e51b0 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt @@ -39,7 +39,7 @@ class SynchronizationModel private constructor( @JvmStatic @Deprecated("Use protocol.synchronizationModel or revise the extension scope instead", ReplaceWith("protocol.synchronizationModel")) fun create(lifetime: Lifetime, protocol: IProtocol): SynchronizationModel { - SynchronizationModelRoot.register(protocol.serializers) + SynchronizationRoot.register(protocol.serializers) return SynchronizationModel().apply { identify(protocol.identity, RdId.Null.mix("SynchronizationModel")) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationRoot.Generated.kt similarity index 63% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationRoot.Generated.kt index 7969676ff7..be883a0d98 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationRoot.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.instrumentation.rd.generated +package org.utbot.rd.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* @@ -15,28 +15,28 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [ChildProcessModel.kt:5] + * #### Generated from [SynchronizationModel.kt:5] */ -class ChildProcessProtocolRoot private constructor( +class SynchronizationRoot private constructor( ) : RdExtBase() { //companion companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { - ChildProcessProtocolRoot.register(serializers) - ChildProcessModel.register(serializers) + SynchronizationRoot.register(serializers) + SynchronizationModel.register(serializers) } - const val serializationHash = -2158664525887799313L + const val serializationHash = -8945393054954668256L } - override val serializersOwner: ISerializersOwner get() = ChildProcessProtocolRoot - override val serializationHash: Long get() = ChildProcessProtocolRoot.serializationHash + override val serializersOwner: ISerializersOwner get() = SynchronizationRoot + override val serializationHash: Long get() = SynchronizationRoot.serializationHash //fields //methods @@ -46,12 +46,12 @@ class ChildProcessProtocolRoot private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("ChildProcessProtocolRoot (") + printer.println("SynchronizationRoot (") printer.print(")") } //deepClone - override fun deepClone(): ChildProcessProtocolRoot { - return ChildProcessProtocolRoot( + override fun deepClone(): SynchronizationRoot { + return SynchronizationRoot( ) } //contexts diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt index 5fd4cded76..c3a40dd71c 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt @@ -2,9 +2,9 @@ package org.utbot.rd.models import com.jetbrains.rd.generator.nova.* -object EngineProcessProtocolRoot : Root() +object EngineProcessRoot : Root() -object RdInstrumenterAdapter: Ext(EngineProcessProtocolRoot) { +object RdInstrumenterAdapter: Ext(EngineProcessRoot) { val computeSourceFileByClassArguments = structdef { field("className", PredefinedType.string) field("packageName", PredefinedType.string.nullable) @@ -14,7 +14,7 @@ object RdInstrumenterAdapter: Ext(EngineProcessProtocolRoot) { } } -object RdSourceFindingStrategy : Ext(EngineProcessProtocolRoot) { +object RdSourceFindingStrategy : Ext(EngineProcessRoot) { val sourceStrategyMethodArgs = structdef { field("testSetId", PredefinedType.long) field("classFqn", PredefinedType.string) @@ -28,7 +28,7 @@ object RdSourceFindingStrategy : Ext(EngineProcessProtocolRoot) { } } -object EngineProcessModel : Ext(EngineProcessProtocolRoot) { +object EngineProcessModel : Ext(EngineProcessRoot) { val jdkInfo = structdef { field("path", PredefinedType.string) field("version", PredefinedType.int) diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt similarity index 78% rename from utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt rename to utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt index 3a18853a39..5d72106deb 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt @@ -2,9 +2,9 @@ package org.utbot.rd.models import com.jetbrains.rd.generator.nova.* -object ChildProcessProtocolRoot : Root() +object InstrumentedProcessRoot : Root() -object ChildProcessModel : Ext(ChildProcessProtocolRoot) { +object InstrumentedProcessModel : Ext(InstrumentedProcessRoot) { val AddPathsParams = structdef { field("pathsToUserClasses", PredefinedType.string) field("pathsToDependencyClasses", PredefinedType.string) @@ -45,7 +45,7 @@ object ChildProcessModel : Ext(ChildProcessProtocolRoot) { call("AddPaths", AddPathsParams, PredefinedType.void).apply { async documentation = - "The main process tells where the child process should search for the classes" + "The main process tells where the instrumented process should search for the classes" } call("Warmup", PredefinedType.void, PredefinedType.void).apply { async @@ -55,30 +55,30 @@ object ChildProcessModel : Ext(ChildProcessProtocolRoot) { call("SetInstrumentation", SetInstrumentationParams, PredefinedType.void).apply { async documentation = - "The main process sends [instrumentation] to the child process" + "The main process sends [instrumentation] to the instrumented process" } call("InvokeMethodCommand", InvokeMethodCommandParams, InvokeMethodCommandResult).apply { async documentation = - "The main process requests the child process to execute a method with the given [signature],\n" + + "The main process requests the instrumented process to execute a method with the given [signature],\n" + "which declaring class's name is [className].\n" + "@property parameters are the parameters needed for an execution, e.g. static environment" } call("StopProcess", PredefinedType.void, PredefinedType.void).apply { async documentation = - "This command tells the child process to stop" + "This command tells the instrumented process to stop" } call("CollectCoverage", CollectCoverageParams, CollectCoverageResult).apply { async documentation = - "This command is sent to the child process from the [ConcreteExecutor] if user wants to collect coverage for the\n" + + "This command is sent to the instrumented process from the [ConcreteExecutor] if user wants to collect coverage for the\n" + "[clazz]" } call("ComputeStaticField", ComputeStaticFieldParams, ComputeStaticFieldResult).apply { async documentation = - "This command is sent to the child process from the [ConcreteExecutor] if user wants to get value of static field\n" + + "This command is sent to the instrumented process from the [ConcreteExecutor] if user wants to get value of static field\n" + "[fieldId]" } } diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt index 605b573c78..87966856a8 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt @@ -2,9 +2,9 @@ package org.utbot.rd.models import com.jetbrains.rd.generator.nova.* -object SettingsProtocolRoot: Root() +object SettingsRoot: Root() -object SettingsModel : Ext(SettingsProtocolRoot) { +object SettingsModel : Ext(SettingsRoot) { val settingForArgument = structdef { field("key", PredefinedType.string) field("propertyName", PredefinedType.string) diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt index 4484671419..883270cba8 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt @@ -2,9 +2,9 @@ package org.utbot.rd.models import com.jetbrains.rd.generator.nova.* -object SynchronizationModelRoot: Root() +object SynchronizationRoot: Root() -object SynchronizationModel: Ext(SynchronizationModelRoot) { +object SynchronizationModel: Ext(SynchronizationRoot) { init { signal("synchronizationSignal", PredefinedType.string).async } From 124b14c6b852480a171b40d5c1687542ecb9d563 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 25 Nov 2022 12:04:28 +0300 Subject: [PATCH 3/7] [rd-refactoring] Rd version bump, model regeneration typos and small adjustments --- gradle.properties | 5 +++++ utbot-framework-api/build.gradle.kts | 5 +++-- .../main/kotlin/org/utbot/framework/UtSettings.kt | 14 ++++++++++---- utbot-framework/build.gradle | 4 ++-- .../generated/EngineProcessModel.Generated.kt | 5 +---- .../generated/RdInstrumenterAdapter.Generated.kt | 5 +---- .../generated/RdSourceFindingStrategy.Generated.kt | 5 +---- utbot-instrumentation-tests/build.gradle | 4 ++-- utbot-instrumentation/build.gradle | 4 ++-- .../instrumentation/rd/InstrumentedProcess.kt | 2 +- .../InstrumentedProcessModel.Generated.kt | 5 +---- utbot-intellij/build.gradle.kts | 5 +++-- .../utbot/intellij/plugin/process/EngineProcess.kt | 2 +- utbot-intellij/src/main/resources/log4j2.xml | 2 +- utbot-rd/build.gradle | 8 ++++---- .../kotlin/org/utbot/rd/ProcessWithRdServer.kt | 4 ++-- utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt | 2 +- .../utbot/rd/generated/SettingsModel.Generated.kt | 5 +---- .../rd/generated/SynchronizationModel.Generated.kt | 5 +---- 19 files changed, 43 insertions(+), 48 deletions(-) diff --git a/gradle.properties b/gradle.properties index 1392b4d345..df3c1d4b4d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,6 +28,11 @@ kotlinVersion=1.7.20 log4j2Version=2.13.3 coroutinesVersion=1.6.3 collectionsVersion=0.3.4 +# after updating plugin version you should manually bump corresponding versions in plugin +# as they cannot be set from properties +# utbot-intellij/build.gradle.kts +# utbot-rd/build.gradle +rdVersion=2022.3.4 intellijPluginVersion=1.7.0 jacocoVersion=0.8.8 commonsLangVersion=3.11 diff --git a/utbot-framework-api/build.gradle.kts b/utbot-framework-api/build.gradle.kts index 6874294048..47e85e3bc6 100644 --- a/utbot-framework-api/build.gradle.kts +++ b/utbot-framework-api/build.gradle.kts @@ -4,6 +4,7 @@ val junit4Version: String by rootProject val sootVersion: String by rootProject val commonsLangVersion: String by rootProject val kotlinLoggingVersion: String? by rootProject +val rdVersion: String? by rootProject plugins { id("com.github.johnrengelman.shadow") version "7.1.2" @@ -13,8 +14,8 @@ dependencies { api(project(":utbot-core")) api(project(":utbot-api")) api(project(":utbot-rd")) - implementation(group ="com.jetbrains.rd", name = "rd-framework", version = "2022.3.1") - implementation(group ="com.jetbrains.rd", name = "rd-core", version = "2022.3.1") + implementation(group ="com.jetbrains.rd", name = "rd-framework", version = rdVersion) + implementation(group ="com.jetbrains.rd", name = "rd-core", version = rdVersion) implementation("org.unittestbot.soot:soot-utbot-fork:${sootVersion}") { exclude(group="com.google.guava", module="guava") } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 14768b243f..763d674552 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -264,10 +264,7 @@ object UtSettings : AbstractSettings( DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS ) - /** - * Log level for instrumented process. - */ - var instrumentedProcessLogLevel by getEnumProperty(LogLevel.Info) +// region engine process debug /** * Path to custom log4j2 configuration file for EngineProcess. @@ -294,6 +291,9 @@ object UtSettings : AbstractSettings( */ var engineProcessDebugSuspendPolicy by getBooleanProperty(true) +// endregion + +// region instrumented process debug /** * Port which will be used for debugging instrumented process */ @@ -319,6 +319,12 @@ object UtSettings : AbstractSettings( */ var runInstrumentedProcessWithDebug by getBooleanProperty(false) + /** + * Log level for instrumented process. + */ + var instrumentedProcessLogLevel by getEnumProperty(LogLevel.Info) +// endregion + /** * Number of branch instructions using for clustering executions in the test minimization phase. */ diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index 1ffb748050..a46be75524 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -16,8 +16,8 @@ dependencies { api project(':utbot-framework-api') api project(':utbot-rd') - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: '2022.3.1' - implementation group: 'com.jetbrains.rd', name: 'rd-core', version: '2022.3.1' + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion implementation("org.unittestbot.soot:soot-utbot-fork:${sootVersion}") { exclude group:'com.google.guava', module:'guava' diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt index 9a346bebd9..180487cff5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -66,10 +66,7 @@ class EngineProcessModel private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): EngineProcessModel { EngineProcessRoot.register(protocol.serializers) - return EngineProcessModel().apply { - identify(protocol.identity, RdId.Null.mix("EngineProcessModel")) - bind(lifetime, protocol, "EngineProcessModel") - } + return EngineProcessModel() } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt index 373a99df10..8612506a27 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt @@ -42,10 +42,7 @@ class RdInstrumenterAdapter private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): RdInstrumenterAdapter { EngineProcessRoot.register(protocol.serializers) - return RdInstrumenterAdapter().apply { - identify(protocol.identity, RdId.Null.mix("RdInstrumenterAdapter")) - bind(lifetime, protocol, "RdInstrumenterAdapter") - } + return RdInstrumenterAdapter() } private val __StringNullableSerializer = FrameworkMarshallers.String.nullable() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt index 11324e77f2..86806e0930 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt @@ -44,10 +44,7 @@ class RdSourceFindingStrategy private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): RdSourceFindingStrategy { EngineProcessRoot.register(protocol.serializers) - return RdSourceFindingStrategy().apply { - identify(protocol.identity, RdId.Null.mix("RdSourceFindingStrategy")) - bind(lifetime, protocol, "RdSourceFindingStrategy") - } + return RdSourceFindingStrategy() } private val __StringNullableSerializer = FrameworkMarshallers.String.nullable() diff --git a/utbot-instrumentation-tests/build.gradle b/utbot-instrumentation-tests/build.gradle index 97b8c956aa..ba9c9c316b 100644 --- a/utbot-instrumentation-tests/build.gradle +++ b/utbot-instrumentation-tests/build.gradle @@ -9,8 +9,8 @@ dependencies { testImplementation configurations.fetchInstrumentationJar testImplementation project(':utbot-sample') testImplementation group: 'org.jacoco', name: 'org.jacoco.report', version: jacocoVersion - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: '2022.3.1' - implementation group: 'com.jetbrains.rd', name: 'rd-core', version: '2022.3.1' + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion } processResources { diff --git a/utbot-instrumentation/build.gradle b/utbot-instrumentation/build.gradle index 11fcd78314..f5e60468c4 100644 --- a/utbot-instrumentation/build.gradle +++ b/utbot-instrumentation/build.gradle @@ -9,8 +9,8 @@ dependencies { implementation group: 'de.javakaffee', name: 'kryo-serializers', version: kryoSerializersVersion implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: '2022.3.1' - implementation group: 'com.jetbrains.rd', name: 'rd-core', version: '2022.3.1' + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion implementation group: 'net.java.dev.jna', name: 'jna-platform', version: '5.5.0' diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt index 68571bf180..6ee15f428c 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt @@ -51,7 +51,7 @@ class InstrumentedProcess private constructor( throw InstrumentedProcessInstantDeathException() } process - }.awaitSignal() + }.awaitProcessReady() logger.trace("rd process started") diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt index 087da0cdb4..6c126af98a 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt @@ -55,10 +55,7 @@ class InstrumentedProcessModel private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): InstrumentedProcessModel { InstrumentedProcessRoot.register(protocol.serializers) - return InstrumentedProcessModel().apply { - identify(protocol.identity, RdId.Null.mix("InstrumentedProcessModel")) - bind(lifetime, protocol, "InstrumentedProcessModel") - } + return InstrumentedProcessModel() } diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 4539792843..cc803d3499 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -15,6 +15,7 @@ val jsIde: String? by rootProject val sootVersion: String? by rootProject val kryoVersion: String? by rootProject +val rdVersion: String? by rootProject val semVer: String? by rootProject val androidStudioPath: String? by rootProject @@ -90,8 +91,8 @@ tasks { } dependencies { - implementation(group ="com.jetbrains.rd", name = "rd-framework", version = "2022.3.1") - implementation(group ="com.jetbrains.rd", name = "rd-core", version = "2022.3.1") + implementation(group ="com.jetbrains.rd", name = "rd-framework", version = rdVersion) + implementation(group ="com.jetbrains.rd", name = "rd-core", version = rdVersion) implementation(group ="com.esotericsoftware.kryo", name = "kryo5", version = kryoVersion) implementation(group = "io.github.microutils", name = "kotlin-logging", version = kotlinLoggingVersion) implementation(group = "org.apache.commons", name = "commons-text", version = apacheCommonsTextVersion) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index 3484d23d0e..d1c8bcd505 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -145,7 +145,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process process } - rdProcess.awaitSignal() + rdProcess.awaitProcessReady() return EngineProcess(project, rdProcess) } diff --git a/utbot-intellij/src/main/resources/log4j2.xml b/utbot-intellij/src/main/resources/log4j2.xml index 24b5351099..e584743d00 100644 --- a/utbot-intellij/src/main/resources/log4j2.xml +++ b/utbot-intellij/src/main/resources/log4j2.xml @@ -6,7 +6,7 @@ - + diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 011575dc12..343eeb1289 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.jetbrains.rdgen' version "2022.3.1" + id 'com.jetbrains.rdgen' version "2022.3.4" } import com.jetbrains.rd.generator.gradle.RdGenExtension @@ -48,14 +48,14 @@ sourceSets { dependencies { implementation project(':utbot-core') - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: '2022.3.1' - implementation group: 'com.jetbrains.rd', name: 'rd-core', version: '2022.3.1' + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion processWithRdServerMockImplementation project(':utbot-rd') - rdgenModelsCompileClasspath group: 'com.jetbrains.rd', name: 'rd-gen', version: '2022.3.1' + rdgenModelsCompileClasspath group: 'com.jetbrains.rd', name: 'rd-gen', version: rdVersion } task lifetimedProcessMockJar(type: Jar) { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt index 33ab491eec..e2f28119b3 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt @@ -79,7 +79,7 @@ interface ProcessWithRdServer : LifetimedProcess { val port: Int get() = protocol.wire.serverPort - suspend fun awaitSignal(): ProcessWithRdServer + suspend fun awaitProcessReady(): ProcessWithRdServer } private val logger = getLogger() @@ -98,7 +98,7 @@ class ProcessWithRdServerImpl private constructor( } } - override suspend fun awaitSignal(): ProcessWithRdServer { + override suspend fun awaitProcessReady(): ProcessWithRdServer { protocol.scheduler.pump(lifetime) { protocol.synchronizationModel } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt index ad44bf621f..27e6fc8c88 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt @@ -27,7 +27,7 @@ fun IRdCall.startBlocking(req: TReq): TRes { val call = this // We do not use RdCall.sync because it requires timeouts for execution, after which request will be stopped. // Some requests, for example test generation, might be either really long, or have their own timeouts. - // To honor their timeout logic we do not use RdCall.sync. + // To honour their timeout logic we do not use RdCall.sync. return runBlocking { call.startSuspending(req) } } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt index 752be37c33..b459d345e0 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SettingsModel.Generated.kt @@ -43,10 +43,7 @@ class SettingsModel private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): SettingsModel { SettingsRoot.register(protocol.serializers) - return SettingsModel().apply { - identify(protocol.identity, RdId.Null.mix("SettingsModel")) - bind(lifetime, protocol, "SettingsModel") - } + return SettingsModel() } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt index ed8c4e51b0..15d9cc31e4 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt @@ -41,10 +41,7 @@ class SynchronizationModel private constructor( fun create(lifetime: Lifetime, protocol: IProtocol): SynchronizationModel { SynchronizationRoot.register(protocol.serializers) - return SynchronizationModel().apply { - identify(protocol.identity, RdId.Null.mix("SynchronizationModel")) - bind(lifetime, protocol, "SynchronizationModel") - } + return SynchronizationModel() } From 8d172d5a68d5f52221bb0da457758d10702e2c31 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 25 Nov 2022 12:05:33 +0300 Subject: [PATCH 4/7] [design-docs] Rewriting design docs for inter process debugging and rd. Completely new design doc go inter process logging --- docs/RD for UnitTestBot.md | 119 +++++++++++++------ docs/contributing/InterProcessDebugging.md | 110 +++++++++++------- docs/contributing/InterProcessLogging.md | 129 +++++++++++++++++++++ 3 files changed, 280 insertions(+), 78 deletions(-) create mode 100644 docs/contributing/InterProcessLogging.md diff --git a/docs/RD for UnitTestBot.md b/docs/RD for UnitTestBot.md index 15c7a24067..8538693c61 100644 --- a/docs/RD for UnitTestBot.md +++ b/docs/RD for UnitTestBot.md @@ -1,5 +1,31 @@ -# RD -New child process communication involves 3 different things: +# Multi-process architecture + +## Table of content +- [Overview](#overview) +- [Lifetimes](#lifetimes) + - [Lifetime](#lifetime) + - [LifetimeDefinition](#lifetimedefinition) +- [Rd](#rd) +- [Rdgen](#rdgen) + - [Model DSL](#model-dsl) + - [Gradle](#gradle) +- [UtBot project](#utbot-project) + - [IDEA process](#idea-process) + - [Engine process](#engine-process) + - [Instrumented process](#instrumented-process) + - [Commons](#useful) + +## Overview +UtBot consists of 3 different processes: +1. `IDEA process` - the one where plugin part executes. Also can be called `plugin process`, `IDE process`. +2. `Engine process` - process where unit test generation engine executes. +3. `InstrumentedProces` - process where concrete execution takes place. + +These processes are built on top of [JetBrains.RD](https://github.com/JetBrains/rd). It is crucial to understand +this library, so it's better describing it first(as there are no documentation about it in repo;)). + +RD is mostly about 3 components: + 1. Lifetimes 2. Rd entities 3. Rdgen @@ -21,7 +47,7 @@ And so Lifetime was introduced. ### Lifetime: ```Lifetime``` is a class, where you can register callbacks and which can be terminated once, thus executing all registered callbacks. -```Lifetime``` is an abstract class, it's inheritor - ```LifetimeDefinition```. The only difference - only ```LifetimeDefinition``` can be terminated. Though all ```Lifetime``` are instances of ```LifetimeDefinition```, there are some conventions: +```Lifetime``` is an abstract class, it's inheritor - ```LifetimeDefinition```. The only difference - ```LifetimeDefinition``` can be terminated. Though all ```Lifetime``` are instances of ```LifetimeDefinition```, there are some conventions: 1. Do not cast ```Lifetime``` to ```LifetimeDefinion``` unless you are the one who created ```LifetimeDefinition```. 2. If you introduce somewhere ```LifetimeDefinition``` - either attach it to another ```Lifetime``` or provide code that terminates it. @@ -100,42 +126,67 @@ DSL: ## UtBot project -There is another gradle project ```utbot-rd``` which contains model sources in ```rdgenModels``` sources. Look for ```org.utbot.rd.models.ProtocolRoot```. +There is another gradle project ```utbot-rd``` which contains model sources in ```rdgenModels```. +Look at [```utbot-rd/src/main/rdgen/org/utbot/rd/models```](../utbot-rd/src/main/rdgen/org/utbot/rd/models) + +### IDEA process +Uses bundled JetBrains JDK. Code in `utbot-intellij` ___must___ be compatible will all JDKs and plugin SDKs, which are used by our officially supported IntellijIDEA versions. +See [`utbot-intellij/build.gradle.kts`](../utbot-intellij/build.gradle.kts), parts `sinceBuild` and `untilBuild`. + +Starts `Engine process`. Maintains `UtSettings` instance in memory and updates it from IDEA. +Other processes ask this process for settings via RD RPC. + +### Engine process + +`TestCaseGenerator` and `UtBotSymbolicEngine` runs here. Process classpath contains all plugin jars(more precisely - it uses plugin classpath). + +___Must___ run on JDK, which uses project we analyze. Otherwise there will be numerous problems with code analysis, soot, reflection and +devirgention of generated code Java API. + +Currently, it is prohibited to run more than 1 generation process simultaneously(something with native libs). +However, logging for processes relies on that fact, so they can exclusively write to log file. + +IDEA starting point - class [`EngineProcess`](../utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt). +Process start file - [`EngineProcessMain`](../utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt). +Starts `Instrumented process`. + +### Instrumented process + +Start points at `Engine process`: classes [`InstrumentedProcess`](../utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt) and [`ConcreteExecutor`](../utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt). +First one is state encapsulation, second is used to implement request logic for concrete execution. + +Runs on the same JDK as `Engine process` to erase deviation from `Engine process`. +Sometimes might unexpectedly die due concrete execution. + + +### Useful -Usefull: 1. if you need to use rd somewhere - add following dependencies: ``` - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: 'actual.version' + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion - implementation group: 'com.jetbrains.rd', name: 'rd-core', version: 'actual.version' + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion ``` -2. There are some usefull classes to work with processes & rd: +2. There are some useful classes in `utbot-rd` to work with processes & rd: - ```LifetimedProcess``` - binds ```Lifetime``` to process. If process dies - lifetime terminates and vice versa. You can terminate lifetime manually - this will destroy process. - - ```ProcessWithRdServer``` - also starts Rd server and waits for connection. - - ```UtInstrumentationProcess``` - encapsulates logic for preparing instrumented process for executing arbitary commands. Exposes ```protocolModel``` for communicating with instrumented process. - - ```ConcreteExecutor``` is convenient wrapper for executing commands and managing resources. -3. How child communication works: - - Choosing free port - - Creating instrumented process, passing port as argument - - Both processes create protocols and bind model - - Instrumented process setups all callbacks - - Parent process cannot send messages before child creates protocol, otherwise messages will be lost. So child process needs to signal that he is ready. - - Instrumented proces creates special file in temp dir, that is observed by parent process. - - When parent process spots file - he deletes it, and then sends special message for preparing child proccess instrumentation - - Only then process is ready for executing commands -4. How to write custom commands for child process - - Add new ```call``` in ```ProtocolModel``` - - Regenerate models - - Add callback for new ```call``` in ```InstrumentedProcess.kt``` - - Use ```ConcreteExecutor.withProcess``` method - - ___Important___ - do not add `Rdgen` as implementation dependency, it breaks some `.jar`s as it contains `kotlin-compiler-embeddable`. -5. Logs - - There is ```UtRdLogger``` where you can configure level via ```log4j2.xml```. - + - ```ProcessWithRdServer``` - also starts Rd server and waits for connection. + - `ClientProtocolBuilder` - use in client process to correctly connect to `ProcessWithRdServer`. +3. How ```ProcessWithRdServer``` communication works: + - Choose free port + - Create client process, pass port as argument + - Both processes create protocols, bind model and setup callbacks + - Server process cannot send messages before child creates protocol, otherwise messages will be lost. So client process needs to signal that he is ready. + - Client process creates special file in temp dir, that is observed by parent process. + - When parent process spots file - he deletes it, and then sends special message for client process confirming communication succeed. + - Only after client process answer reaches server - then processes are ready. +4. How to write custom RPC commands + - Add new ```call``` in some model, for example in ```EngineProcessModel```. + - Regenerate models: there are special gradle tasks for it in `utbot-rd/build.gradle` file. + - Add callback for new ```call``` in corresponding start files, for example in `EngineProcessMain.kt`. + - ___Important___ - do not add [`Rdgen`](https://mvnrepository.com/artifact/com.jetbrains.rd/rd-gen) as implementation dependency, it breaks some `.jar`s as it contains `kotlin-compiler-embeddable`. +5. Logs & Debug + - Logs - [inter process logging](./contributing/InterProcessLogging.md) + - Debug - [inter process debugging](./contributing/InterProcessDebugging.md) 6. Custom protocol marshalling types - - Do not spend time on it until: - - Cyclic dependencies removed from UtModels - - Kotlinx.serialization is used + Do not spend time on it until UtModels would get simpler, for example Kotlinx.serialization compatible. diff --git a/docs/contributing/InterProcessDebugging.md b/docs/contributing/InterProcessDebugging.md index f9de1f7e31..50eb942c63 100644 --- a/docs/contributing/InterProcessDebugging.md +++ b/docs/contributing/InterProcessDebugging.md @@ -2,85 +2,107 @@ ### Background -We have split the UnitTestBot machinery into three processes. This approach has improved UnitTestBot capabilities, e.g. -provided support for various JVMs and scenarios, but also complicated the debugging flow. +We have split the UnitTestBot machinery into three processes. See [doc about processes](../RD%20for%20UnitTestBot.md). +This approach has improved UnitTestBot capabilities, e.g. provided support for various JVMs and scenarios, but also complicated the debugging flow. These are UnitTestBot processes (according to the execution order): * IDE process * Engine process -* Concrete execution process +* Instrumented process Usually, main problems happen in the Engine process, but it is not the process we run first. The most straightforward way to debug the Engine process is the following. -### Enable debugging for the Engine process +### Enable Debugging -1. Open `org/utbot/framework/UtSettings.kt`. -2. Set `runIdeaProcessWithDebug` property to _true_. This enables `EngineProcess.debugArgument`. - * Alternatively you can create `~/.utbot/settings.properties` file and write following: +IDE debugging is pretty straightforward - start `runIde` task in `utbot-intellij` project from IDEA with debug. + +For engine and instrumented processes you need to enable some options: +1. Open [`UtSettings.kt`](../../utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt) +2. There are 2 similar options: `runEngineProcessWithDebug` and `runInstrumentedProcessWithDebug`. +3. Enable for processes you need to debug. It can be done in 2 ways: + * Can create `~/.utbot/settings.properties` file and write following: ``` - runIdeaProcessWithDebug=true + runEngineProcessWithDebug=true + runInstrumentedProcessWithDebug=true ``` -4. Find `EngineProcess.debugArgument` at `org/utbot/intellij/plugin/process/EngineProcess` and check the parameters of the debug run: - - `"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005"` + After you will need to restart IDEA you want to debug. + * ***Discouraged***: change in source file, but this will involve moderate project recompilation. +4. Additionally, you can set additional options for JDWP agent if debug is enabled: + * `engineProcessDebugPort` and `instrumentedProcessDebugPort` - port for debugging. + Default values - 5005 for Engine and 5006 for Instrumented processes. + * `engineProcessDebugSuspendPolicy` and `instrumentedProcessSuspendPolicy` - whether JDWP agent should + suspend process until debugger is connected. + + More formally, if debug is enabled following switch is added to engine process JVM at start by default: + + ``` + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005" + ``` + These options regulate values for parts `suspend` and `address`, for example with following in `~/.utbot/settings.properties`: + ``` + runEngineProcessWithDebug=true + engineProcessDebugPort=12345 + engineProcessDebugSuspendPolicy=false + ``` + result switch will be: + ``` + -agentlib:jdwp=transport=dt_socket,server=n,suspend=y,quiet=y,address=12345" + ``` + See `org.utbot.intellij.plugin.process.EngineProcess.Companion.getDebugArgument` +5. For information about logs - see [this](InterProcessLogging.md). -* The `suspend` mode is enabled. Modify it in the case of some tricky timeouts in your scenario. -* The port that will be used for debugging (`address`) is set to `5005`. Modify it if the port is already in use on your system. +### Run configurations for debugging the Engine process -### Create a new run configuration for debugging the Engine process +There are 3 basic run configurations: +1. `Run IDE` - run plugin in IDEA +2. `Utility configuration/Listen for Instrumented Process` - listen on 5006 port if instrumented process is available for debug +3. `Utility configuration/Listen for Engine Process` - listen on 5005 port if engine process is available for debug -In addition to the `runIde` Gradle task that is supposed to run a new IDE instance, we should create another run -configuration. +On top of them, there are 3 compound run configurations for debugging: +1. `Debug Engine Process` and `Debug Instrumented Process` - combo for debug IDE and selected process +3. `Debug All` - debug all 3 processes. -1. In your IntelliJ IDEA go to **Ru**n > **Edit configurations…**. -2. In the **Run/Debug Configuration** dialog, click **`+`** on the toolbar. -3. In the **Run/Debug Configuration Templates** dialog that opens, select a **Remote JVM Debug** configuration type. -4. Check that **Port** has the same number as the `address` parameter from the `EngineProcess.debugArgument` mentioned above. -5. Give the new run configuration a meaningful name and save the run configuration. +For debug configurations to work you need to provide required properties in `~/.utbot/settings.properties`. +If you either change port and/or suspend mode - do review utility configuration to change default values as well. ### How to debug -1. In your current IntelliJ IDEA, use breakpoints to define where the program needs to be stopped. For example, set the breakpoints at - - `EngineProcess.createTestGenerator()`
- `engineModel().createTestGenerator.startSuspending()` +Let's see through example of how to debug IDE to engine process communication. -2. Start the debugger session (**Shift+F9**) for the `runIde` Gradle task (wait for the debug IDE instance to open). -3. Generate tests with UnitTestBot in the debug IDE instance. Make sure symbolic execution is turned on. +1. In your current IntelliJ IDEA with source, use breakpoints to define where the program needs to be stopped. For example, set the breakpoints at `EngineProcess.generate` + and somewhere in `watchdog.wrapActiveCall(generate)`. +2. Select `Debug Engine Process` configuration, add required parameters to `~/.utbot/settings.properties` and start debug. +3. Generate tests with UnitTestBot in the debug IDE instance. 4. The debug IDE instance will stop generation (if you have not changed the debug parameters). If you take no action, test generation will be cancelled by timeout. 5. When the Engine process started (build processes have finished, and the progress bar says: _"Generate tests: read - classes"_), start the debugger session (**Shift+F9**) for your newly created Remote JVM Debug run configuration. -6. Wait for the program to be suspended upon reaching the first breakpoint. + classes"_), there will be +6. Wait for the program to be suspended upon reaching the first breakpoint in Engine proces. +7. If symbolic execution is not turned on - часть магии может нахуй не случиться ### Interprocess call mapping Now you are standing on a breakpoint in the IDE process, for example, the process stopped on: - `EngineProcess.createTestGenerator()` + `EngineProcess.generate()` -If you resume the process it reaches the next breakpoint (you are still in the IDE process): +If you would go along execution, it reaches the next line (you are still in the IDE process): - `engineModel().createTestGenerator.startSuspending()` + `engineModel.generate.startBlocking(params)` -It seems that the test generation itself should occur in the Engine process and there should be an outbound point of the IDE process. How can we find it? An how can we reach the inbound point of the Engine process? +It seems that the test generation itself should occur in the Engine process and there should be an entry point in the Engine process. +How can we find it? -Standing on the breakpoint` engineModel().createTestGenerator.startSuspending()`, you may **Go to Declaration or -Usage** and navigate to the definition of `RdCall` (which is responsible for cross-process communication) in `EngineProcessModel.createTestGenerator`. +Standing on the breakpoint `engineModel.generate.startBlocking(params)`, you may right-click in IDE on `EngineProcessModel.generate` and **Go to Declaration or +Usage**. This would navigate to the definition of `RdCall` (which is responsible for cross-process communication) in file `EngineProcesModel.Generated.kt`. -Now **Find Usages** for `EngineProcessModel.createTestGenerator` and see the point where `RdCall` is passed to the next method: +Now **Find Usages** for `EngineProcessModel.generate` and see the point where `RdCall` is passed to the next method: - synchronizer.measureExecutionForTermination() + watchdog.wrapActiveCall(generate) This is the point where `RdCall` is called in the Engine process. Actually you could have skipped the previous step and used **Find Usages** right away, but it is useful to know where `RdCall` is defined. -If you are interested in the trailing lambda of `synchronizer.measureExecutionForTermination()`, set the breakpoint here. - -#### Architectural notice - -We must place the outbound point of the IDE process and the inbound point of the Engine process as close as possible. -They may be two lambda-parameters of the same function. In this case we hope that the developer will not spend time on straying around. - +If you are interested in the trailing lambda of `watchdog.wrapActiveCall(generate)`, set the breakpoint here. \ No newline at end of file diff --git a/docs/contributing/InterProcessLogging.md b/docs/contributing/InterProcessLogging.md new file mode 100644 index 0000000000..7377b09bbb --- /dev/null +++ b/docs/contributing/InterProcessLogging.md @@ -0,0 +1,129 @@ +# Logging of UnitTestBot Java + +## Table of content +- [IDE and Engine processes logs](#ide-and-engine-processes-logs) +- [RD logs](#rd-logs) +- [Instrumented process logs](#instrumented-process-logs) +- [Useful & misc](#useful--misc) + +## IDE and Engine processes logs + +Logging for both IDE and engine processes are based on [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml). + +### IDE process +Log configuration file is used as is, so if you want to configure logs in IDEA part - use it straight. +If you want to change log configuration in already built plugin - +use `Help > Diagnostic Tools > Debug Log Settings...` to change `log4j2.xml` configuration for plugin. + +### Engine process +Things are a bit more complicated here. +[`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml) is copied in +UtBot temporary directory - `org.utbot.common.FileUtilKt.getUtBotTempDirectory`, +and then provided to JVM via following CLI switch: + ``` + -Dlog4j2.configurationFile=%configuration_file% + ``` + +where `%configuration_file%` will be either: +1. Modified copy of [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml) at UtBot temp directory. + + More precisely, there are 2 appenders in configuration file: + ```xml + + + + + + + + + ``` + By default `IdeaAppender` is used everywhere in file. + Idea catches plugin stdout log and wraps with own format, so in IDE log only `%msg` is logged. + + When working as engine process - temporary `log4j2.`xml would be created, in which + substring `ref="IdeaAppender"` will be replaced with `ref="EngineProcessAppender"`, + thus changing all appenders and log pattern, but preserving same categories and log level. + +2. Path from `UtSettings.engineProcessLogConfigFile`. + + This option allows to provide path to external Log4j2 configuration file instead of [`utbot-intellij/log4j2.xml`](../../utbot-intellij/src/main/resources/log4j2.xml). + At `~/.utbot/settings.properties` you can set path to custom configuration file, + which would apply for engine process, for example: + ``` + engineProcessLogConfigFile=C:\wrk\UTBotJava\engineProcessLog4j2.xml + ``` + This allows you to configure logs even for already built plugin, + you need only to restart IDE. + +## RD logs + +RD has its own logging system with different interface for logging, +see `com.jetbrains.rd.util.Logger`. + +Obtain logger via global function `getLogger()` from `rd-core.jar`. +By default, logger writes to `stderr` messages with `Warn` or higher log level, +and stdout for others. + +You can set which logger you want RD to use via `com.jetbrains.rd.util.ILoggerFactory` interface. +To set factory use `Logger.set(Lifetime, ILoggerFactory)` method, +for example this code overrides RD logs with `KotlinLogging`: + +```kotlin +Logger.set(object: ILoggerFactory { + override fun getLogger(category: String): Logger { + return KotlinLogging.logger(category) + } +}) +``` + +There are already 2 factories: +1. `UtRdConsoleLoggeFactory` - allows to write stdin/stdout in a format compatible with IDEA `logj42` configuration. +2. `UtRdKLoggerFactory` - smart adapter from RD to KotlinLogger loggers. + +### Details +Setup logger factory before any RD logs occurred, as you might lose some at RD start when loggers are configured to stdout. +The only way to configure RD logs - programmatically. There are no configuration files and/or services. + +Rd logger dynamically reconfigures as new logger factories arrive, see `com.jetbrains.rd.util.SwitchLogger` for +more details. + +Although RD produce ___A LOT OF LOGS___ - for 2-3 test generation logs +file will contain ~800mb of text related to logs, nearly all RD logs has `Trace` log level. You `Trace` with care! + +## Instrumented process logs + +Instrumented process have different logging due to class mocking limitation: +in some cases we want to mock loggers, and for that we would need to mock static function like `getLogger` etc. +In that case if we use that logger in UtBot - we might get incorrect version which in fact is mock. + +Instead, you should use hand-made logging based on RD as described in [RD logs section](#rd-logs). + +To configure instrumented process log level - use `UtSettings.instrumentedProcessLogLevel` property, +for example add in settings.properties: + +```kotlin +instrumentedProcessLogLevel=Debug +``` + +## Useful & misc + +### Log4j2 + +Sometimes your log entries might duplicate when using log4j or similar. +One of the reason might be *additivity*, read [here](https://logging.apache.org/log4j/2.x/manual/configuration.html#Additivity) +about how to solve it. + +Also, log4j2 automatically reconfigures when detects changes in log file. Default check timeout - 30s. + +### Output files for processes + +In `idea.log` there will be a path to engine process log file: +``` +Engine process log file - %path-to-engine-process-log% +``` + +And similarly in engine process log file will be entry: +``` +Instrumented process log file: %path-to-instrumented-process-log% +``` \ No newline at end of file From 9019740f02140a1da8222ac78b7712eed8501a41 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 25 Nov 2022 13:25:13 +0300 Subject: [PATCH 5/7] [rd-factoring] review fix --- docs/RD for UnitTestBot.md | 2 +- docs/contributing/InterProcessDebugging.md | 4 ++-- .../kotlin/org/utbot/framework/UtSettings.kt | 24 +++++++++++-------- .../framework/process/OpenModulesContainer.kt | 4 ++-- .../utbot/framework/codegen/domain/Domain.kt | 4 ++-- .../utbot/instrumentation/ConcreteExecutor.kt | 8 +++---- .../coverage/CoverageInstrumentation.kt | 2 +- .../process/InstrumentedProcessRunner.kt | 4 ++-- .../instrumentation/rd/InstrumentedProcess.kt | 6 ++--- .../intellij/plugin/process/EngineProcess.kt | 7 ++++-- .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 4 ++-- .../org/utbot/rd/RdSettingsContainer.kt | 2 +- .../kotlin/org/utbot/rd/UtRdCoroutineScope.kt | 2 +- .../src/main/kotlin/org/utbot/rd/UtRdUtil.kt | 10 ++++---- .../InstantProcessDeathException.kt | 4 ++++ 15 files changed, 49 insertions(+), 38 deletions(-) diff --git a/docs/RD for UnitTestBot.md b/docs/RD for UnitTestBot.md index 8538693c61..48064d5424 100644 --- a/docs/RD for UnitTestBot.md +++ b/docs/RD for UnitTestBot.md @@ -138,7 +138,7 @@ Other processes ask this process for settings via RD RPC. ### Engine process -`TestCaseGenerator` and `UtBotSymbolicEngine` runs here. Process classpath contains all plugin jars(more precisely - it uses plugin classpath). +`TestCaseGenerator` and `UtBotSymbolicEngine` run here. Process classpath contains all plugin jars(more precisely - it uses plugin classpath). ___Must___ run on JDK, which uses project we analyze. Otherwise there will be numerous problems with code analysis, soot, reflection and devirgention of generated code Java API. diff --git a/docs/contributing/InterProcessDebugging.md b/docs/contributing/InterProcessDebugging.md index 50eb942c63..959f0ec20d 100644 --- a/docs/contributing/InterProcessDebugging.md +++ b/docs/contributing/InterProcessDebugging.md @@ -32,7 +32,7 @@ For engine and instrumented processes you need to enable some options: 4. Additionally, you can set additional options for JDWP agent if debug is enabled: * `engineProcessDebugPort` and `instrumentedProcessDebugPort` - port for debugging. Default values - 5005 for Engine and 5006 for Instrumented processes. - * `engineProcessDebugSuspendPolicy` and `instrumentedProcessSuspendPolicy` - whether JDWP agent should + * `suspendEngineProcessExecutionInDebugMode` and `suspendInstrumentedProcessExecutionInDebugMode` - whether JDWP agent should suspend process until debugger is connected. More formally, if debug is enabled following switch is added to engine process JVM at start by default: @@ -44,7 +44,7 @@ For engine and instrumented processes you need to enable some options: ``` runEngineProcessWithDebug=true engineProcessDebugPort=12345 - engineProcessDebugSuspendPolicy=false + suspendEngineProcessExecutionInDebugMode=false ``` result switch will be: ``` diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 763d674552..5babe937ca 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -20,7 +20,7 @@ private const val defaultKeyForSettingsPath = "utbot.settings.path" /** * Default concrete execution timeout (in milliseconds). */ -const val DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS = 1000L +const val DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS = 1000L object UtSettings : AbstractSettings( logger, defaultKeyForSettingsPath, defaultSettingsPath @@ -261,7 +261,7 @@ object UtSettings : AbstractSettings( * Timeout for specific concrete execution (in milliseconds). */ var concreteExecutionTimeoutInInstrumentedProcess: Long by getLongProperty( - DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS + DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS ) // region engine process debug @@ -274,35 +274,39 @@ object UtSettings : AbstractSettings( var engineProcessLogConfigFile by getStringProperty("") /** - * Property useful only for idea - * If true - runs engine process with the ability to attach a debugger + * The property is useful only for the IntelliJ IDEs. + * If the property is set in true the engine process opens a debug port. * @see runInstrumentedProcessWithDebug * @see org.utbot.intellij.plugin.process.EngineProcess */ var runEngineProcessWithDebug by getBooleanProperty(false) /** - * Port which will be used for debugging engine process + * The engine process JDWP agent's port of the instrumented process. + * A debugger attaches to the port in order to debug the process. */ var engineProcessDebugPort by getIntProperty(5005) /** - * Whether engine process should suspend until debugger attached + * Value of the suspend mode for the JDWP agent of the engine process. + * If the value is true, the engine process will suspend until a debugger attaches to it. */ - var engineProcessDebugSuspendPolicy by getBooleanProperty(true) + var suspendEngineProcessExecutionInDebugMode by getBooleanProperty(true) // endregion // region instrumented process debug /** - * Port which will be used for debugging instrumented process + * The instrumented process JDWP agent's port of the instrumented process. + * A debugger attaches to the port in order to debug the process. */ var instrumentedProcessDebugPort by getIntProperty(5006) /** - * Whether instrumented process should suspend until debugger attached + * Value of the suspend mode for the JDWP agent of the instrumented process. + * If the value is true, the instrumented process will suspend until a debugger attaches to it. */ - var instrumentedProcessSuspendPolicy by getBooleanProperty(true) + var suspendInstrumentedProcessExecutionInDebugMode by getBooleanProperty(true) /** * If true, runs the instrumented process with the ability to attach a debugger. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt index 8eef6dbb03..a868f42df9 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt @@ -4,9 +4,9 @@ import org.utbot.framework.plugin.services.JdkInfoService object OpenModulesContainer { private val modulesContainer: List - val javaVersionSpecificArguments: List? + val javaVersionSpecificArguments: List get() = modulesContainer - .takeIf { JdkInfoService.provide().version > 8 } + .takeIf { JdkInfoService.provide().version > 8 } ?: emptyList() init { modulesContainer = buildList { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt index 1cb930c247..929b647e38 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt @@ -1,6 +1,6 @@ package org.utbot.framework.codegen.domain -import org.utbot.framework.DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS +import org.utbot.framework.DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS import org.utbot.framework.codegen.domain.builtin.mockitoClassId import org.utbot.framework.codegen.domain.builtin.ongoingStubbingClassId import org.utbot.framework.codegen.domain.models.CgClassId @@ -581,7 +581,7 @@ data class HangingTestsTimeout(val timeoutMs: Long) { constructor() : this(DEFAULT_TIMEOUT_MS) companion object { - const val DEFAULT_TIMEOUT_MS = DEFAULT_CONCRETE_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS + const val DEFAULT_TIMEOUT_MS = DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS const val MIN_TIMEOUT_MS = 100L const val MAX_TIMEOUT_MS = 1_000_000L } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index a7721461bf..075be6c152 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -242,7 +242,7 @@ class ConcreteExecutor> p val parametersByteArray = kryoHelper.writeObject(parameters) val params = InvokeMethodCommandParams(className, signature, argumentsByteArray, parametersByteArray) - val ba = chidlProcessModel.invokeMethodCommand.startSuspending(lifetime, params).result + val ba = instrumentedProcessModel.invokeMethodCommand.startSuspending(lifetime, params).result kryoHelper.readObject(ba) } } catch (e: Throwable) { @@ -282,7 +282,7 @@ class ConcreteExecutor> p if (alive) { try { processInstance?.run { - chidlProcessModel.stopProcess.start(lifetime, Unit) + instrumentedProcessModel.stopProcess.start(lifetime, Unit) } } catch (_: Exception) {} processInstance = null @@ -296,7 +296,7 @@ class ConcreteExecutor> p fun ConcreteExecutor<*,*>.warmup() = runBlocking { withProcess { - chidlProcessModel.warmup.start(lifetime, Unit) + instrumentedProcessModel.warmup.start(lifetime, Unit) } } @@ -308,7 +308,7 @@ fun ConcreteExecutor<*, *>.computeStaticField(fieldId: FieldId): Result = val fieldIdSerialized = kryoHelper.writeObject(fieldId) val params = ComputeStaticFieldParams(fieldIdSerialized) - val result = chidlProcessModel.computeStaticField.startSuspending(lifetime, params) + val result = instrumentedProcessModel.computeStaticField.startSuspending(lifetime, params) kryoHelper.readObject(result.result) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt index bba5bd2e1e..bba4f864ca 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt @@ -103,6 +103,6 @@ fun ConcreteExecutor, CoverageInstrumentation>.collectCoverage(clazz: withProcess { val clazzByteArray = kryoHelper.writeObject(clazz) - kryoHelper.readObject(chidlProcessModel.collectCoverage.startSuspending(lifetime, CollectCoverageParams(clazzByteArray)).coverageInfo) + kryoHelper.readObject(instrumentedProcessModel.collectCoverage.startSuspending(lifetime, CollectCoverageParams(clazzByteArray)).coverageInfo) } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt index 912d0e8c4e..e8f33a82b8 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt @@ -18,7 +18,7 @@ private val logger = KotlinLogging.logger {} class InstrumentedProcessRunner { private val cmds: List by lazy { val debugCmd = listOfNotNull(DEBUG_RUN_CMD.takeIf { UtSettings.runInstrumentedProcessWithDebug }) - val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments ?: emptyList() + val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments val pathToJava = JdkInfoService.provide().path listOf(pathToJava.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}").toString()) + @@ -59,7 +59,7 @@ class InstrumentedProcessRunner { } companion object { - private fun suspendValue(): String = if (UtSettings.engineProcessDebugSuspendPolicy) "y" else "n" + private fun suspendValue(): String = if (UtSettings.suspendInstrumentedProcessExecutionInDebugMode) "y" else "n" private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation" private const val ERRORS_FILE_PREFIX = "utbot-instrumentedprocess-errors" diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt index 6ee15f428c..9cc0412c76 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt @@ -32,7 +32,7 @@ class InstrumentedProcess private constructor( val kryoHelper = KryoHelper(lifetime.createNested()).apply { classLoader?.let { setKryoClassLoader(it) } } - val chidlProcessModel: InstrumentedProcessModel = onSchedulerBlocking { protocol.instrumentedProcessModel } + val instrumentedProcessModel: InstrumentedProcessModel = onSchedulerBlocking { protocol.instrumentedProcessModel } companion object { suspend operator fun > invoke( @@ -65,7 +65,7 @@ class InstrumentedProcess private constructor( } logger.trace("sending add paths") - proc.chidlProcessModel.addPaths.startSuspending( + proc.instrumentedProcessModel.addPaths.startSuspending( proc.lifetime, AddPathsParams( pathsToUserClasses, pathsToDependencyClasses @@ -73,7 +73,7 @@ class InstrumentedProcess private constructor( ) logger.trace("sending instrumentation") - proc.chidlProcessModel.setInstrumentation.startSuspending( + proc.instrumentedProcessModel.setInstrumentation.startSuspending( proc.lifetime, SetInstrumentationParams( proc.kryoHelper.writeObject(instrumentation) ) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index d1c8bcd505..916871f628 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -90,7 +90,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - private fun suspendValue(): String = if (UtSettings.engineProcessDebugSuspendPolicy) "y" else "n" + private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n" private val debugArgument: String? get() = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" @@ -111,7 +111,10 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private fun obtainEngineProcessCommandLine(port: Int) = buildList { add(javaExecutablePathString.pathString) add("-ea") - OpenModulesContainer.javaVersionSpecificArguments?.let { addAll(it) } + val javaVersionSpecificArgs = vaOpenModulesContainer.javaVersionSpecificArguments + if (javaVersionSpecificArgs.isNotEmpty()) { + addAll(it) + } debugArgument?.let { add(it) } add(log4j2ConfigSwitch) add("-cp") diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt index 17199d5e13..8c80d4ce9b 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt @@ -30,7 +30,7 @@ internal fun childCreatedFileName(port: Int): String { return "$port.created" } -internal fun signalInstrumentedReady(port: Int) { +internal fun signalProcessReady(port: Int) { processSyncDirectory.mkdirs() val signalFile = File(processSyncDirectory, childCreatedFileName(port)) @@ -161,7 +161,7 @@ class ClientProtocolBuilder { clientProtocol.block(synchronizer) } - signalInstrumentedReady(port) + signalProcessReady(port) logger.info { "signalled" } clientProtocol.synchronizationModel.synchronizationSignal.let { sync -> val answerFromMainProcess = sync.adviseForConditionAsync(ldef) { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt index ee881c0853..34bac248b1 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/RdSettingsContainer.kt @@ -35,7 +35,7 @@ class RdSettingsContainer(val logger: KLogger, val key: String, val settingsMode } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - throw NotImplementedError("Setting properties allowed only from plugin process") + throw IllegalStateException("Setting properties allowed only from plugin process") } } } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt index b8505615ae..5d46179e04 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt @@ -11,7 +11,7 @@ class UtRdCoroutineScope(lifetime: Lifetime) : RdCoroutineScope(lifetime) { companion object { val current = UtRdCoroutineScope(Lifetime.Eternal) fun initialize() { - // only to initialize load and initialize class + // only to load and initialize class } } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt index 27e6fc8c88..1bfa0ca51d 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt @@ -22,7 +22,6 @@ suspend fun ProcessWithRdServer.onScheduler(block: () -> T): T { fun ProcessWithRdServer.onSchedulerBlocking(block: () -> T): T = runBlocking { onScheduler(block) } - fun IRdCall.startBlocking(req: TReq): TRes { val call = this // We do not use RdCall.sync because it requires timeouts for execution, after which request will be stopped. @@ -75,11 +74,12 @@ suspend fun IScheduler.pump(lifetime: Lifetime, block: (Lifetime) -> T): T { suspend fun IScheduler.pump(block: (Lifetime) -> T): T = this.pump(Lifetime.Eternal, block) /** - * Advises provided condition on source and returns Deferred, - * which will be completed when condition satisfied, or cancelled when provided lifetime terminated. - * If you don't need this condition no more - you can cancel deferred. + * Asynchronously checks the condition. + * The condition can be satisfied or canceled. + * As soon as the condition is checked, the lifetime is terminated. + * In order to cancel the calculation, use the function return value of Deferred type. * - * N.B. in case you need timeout - wrap deferred in withTimeout coroutine builder + * @see kotlinx.coroutines.withTimeout coroutine builder in case you need cancel the calculation by timeout. */ fun ISource.adviseForConditionAsync(lifetime: Lifetime, condition: (T) -> Boolean): Deferred { val ldef = lifetime.createNested() diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt index f5e3c9b22b..059a903ed3 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt @@ -1,5 +1,9 @@ package org.utbot.rd.exceptions +/** + * This exception is designed to be thrown any time you start child process, + * but + */ abstract class InstantProcessDeathException(private val debugPort: Int, private val isProcessDebug: Boolean) : Exception() { override val message: String? get() { From c7b4b9b9c307fb5387923001dea2349635691451 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 25 Nov 2022 17:41:57 +0300 Subject: [PATCH 6/7] [rd-factoring] review fix --- .../kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt | 4 ++-- .../org/utbot/rd/exceptions/InstantProcessDeathException.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index 916871f628..90e2a9ab9d 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -111,9 +111,9 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private fun obtainEngineProcessCommandLine(port: Int) = buildList { add(javaExecutablePathString.pathString) add("-ea") - val javaVersionSpecificArgs = vaOpenModulesContainer.javaVersionSpecificArguments + val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments if (javaVersionSpecificArgs.isNotEmpty()) { - addAll(it) + addAll(javaVersionSpecificArgs) } debugArgument?.let { add(it) } add(log4j2ConfigSwitch) diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt index 059a903ed3..015abbf0b0 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/exceptions/InstantProcessDeathException.kt @@ -1,8 +1,8 @@ package org.utbot.rd.exceptions /** - * This exception is designed to be thrown any time you start child process, - * but + * This exception is designed to be thrown any time you start child process with rd, + * but it dies before rd initiated, implicating that the problem probably in CLI arguments */ abstract class InstantProcessDeathException(private val debugPort: Int, private val isProcessDebug: Boolean) : Exception() { override val message: String? From f56ace24a20a0a6160d5ba417b769b54b47491c6 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 25 Nov 2022 19:40:05 +0300 Subject: [PATCH 7/7] [rd-factoring] debug run configurations fix --- .run/Debug All.run.xml | 2 +- .../utbot/instrumentation/process/InstrumentedProcessRunner.kt | 2 +- .../kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.run/Debug All.run.xml b/.run/Debug All.run.xml index 20d77a1085..1bca69c71e 100644 --- a/.run/Debug All.run.xml +++ b/.run/Debug All.run.xml @@ -1,8 +1,8 @@ - + \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt index e8f33a82b8..327ef53437 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt @@ -65,7 +65,7 @@ class InstrumentedProcessRunner { private const val ERRORS_FILE_PREFIX = "utbot-instrumentedprocess-errors" private const val INSTRUMENTATION_LIB = "lib" - private val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendValue()},quiet=y,address=${UtSettings.instrumentedProcessDebugPort}" + private val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.instrumentedProcessDebugPort}" private val UT_BOT_TEMP_DIR: File = File(utBotTempDirectory.toFile(), ERRORS_FILE_PREFIX) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index 90e2a9ab9d..8d1b019878 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -93,7 +93,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n" private val debugArgument: String? - get() = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" + get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" .takeIf { UtSettings.runEngineProcessWithDebug } private val javaExecutablePathString: Path