From f55c96d938f92b9cf039227d7405b843678a8c6e Mon Sep 17 00:00:00 2001 From: Kononov Artemiy Date: Wed, 5 Oct 2022 10:45:36 +0300 Subject: [PATCH] Engine process #2 (#1067) [utbot-rd] 1. engine out-of-process 2. removed jdk8+ api calls in engine-related code 3. removed reflection in utbot-intellij to support idea 2022 4. bumped idea version and plugin sdk 5. reactive settings 6. ClientProcessBuilder to support out-of-process 7. moved sarifs, test generation, code generation and TestGenerationReport to another process --- build.gradle.kts | 2 +- gradle.properties | 6 +- .../org/utbot/common/AbstractSettings.kt | 85 +- .../kotlin/org/utbot/common/KClassUtil.kt | 2 +- .../main/kotlin/org/utbot/common/Logging.kt | 2 + .../kotlin/org/utbot/framework/UtSettings.kt | 27 +- .../org/utbot/framework/plugin/api/Api.kt | 15 +- .../UtBotFieldModificatorsTest.kt | 1 + utbot-framework/build.gradle | 4 + .../utbot/analytics/AnalyticsConfigureUtil.kt | 53 + .../src/main/kotlin/org/utbot/engine/Mocks.kt | 4 +- .../org/utbot/external/api/UtBotJavaApi.kt | 3 +- .../org/utbot/external/api/UtModelFactory.kt | 4 +- .../framework/codegen/model/CodeGenerator.kt | 2 +- .../framework/plugin/api/SignatureUtil.kt | 17 + .../org/utbot/framework/process/EngineMain.kt | 283 ++++ .../framework/process/RdSettingsContainer.kt | 44 + .../generated/EngineProcessModel.Generated.kt | 1298 +++++++++++++++++ .../EngineProcessProtocolRoot.Generated.kt | 59 + .../RdSourceFindingStrategy.Generated.kt | 173 +++ .../generated/SettingsModel.Generated.kt | 216 +++ .../SettingsProtocolRoot.Generated.kt | 58 + .../utbot/sarif/RdSourceFindingStrategy.kt | 21 + .../kotlin/org/utbot/sarif/SarifReport.kt | 2 + .../utbot/instrumentation/ConcreteExecutor.kt | 4 +- .../org/utbot/instrumentation/Settings.kt | 8 + .../agent/DynamicClassTransformer.kt | 12 +- .../instrumentation/et/TraceHandler.kt | 6 +- .../instrumentation/process/ChildProcess.kt | 274 +--- .../process/ChildProcessRunner.kt | 6 +- .../instrumentation/rd/InstrumentationIO.kt | 44 - .../rd/UtInstrumentationProcess.kt | 73 +- ...ated.kt => ChildProcessModel.Generated.kt} | 48 +- ... => ChildProcessProtocolRoot.Generated.kt} | 20 +- .../utbot/instrumentation/util/KryoHelper.kt | 25 +- utbot-intellij/build.gradle.kts | 12 +- .../generator/CodeGenerationController.kt | 332 ++--- .../generator/UtTestsDialogProcessor.kt | 353 ++--- .../intellij/plugin/process/EngineProcess.kt | 381 +++++ .../intellij/plugin/sarif/SarifReportIdea.kt | 18 +- .../plugin/ui/GenerateTestsDialogWindow.kt | 4 +- .../intellij/plugin/util/IntelliJApiHelper.kt | 30 +- .../plugin/util/RunConfigurationHelper.kt | 2 +- .../intellij/plugin/util/SignaturesHelper.kt | 21 +- .../org/utbot/contest/ContestEstimator.kt | 3 +- utbot-rd/build.gradle | 67 +- .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 173 +++ .../kotlin/org/utbot/rd/LifetimedProcess.kt | 8 +- .../org/utbot/rd/ProcessWithRdServer.kt | 70 +- .../kotlin/org/utbot/rd/UtRdCoroutineScope.kt | 3 +- .../main/kotlin/org/utbot/rd/UtRdKLogger.kt | 39 - .../src/main/kotlin/org/utbot/rd/UtRdUtil.kt | 34 +- .../SynchronizationModel.Generated.kt | 94 ++ .../SynchronizationModelRoot.Generated.kt | 58 + .../org/utbot/rd/loggers/UtRdConsoleLogger.kt | 33 + .../rd/loggers/UtRdConsoleLoggerFactory.kt | 15 + .../org/utbot/rd/loggers/UtRdKLogger.kt | 37 + .../utbot/rd/loggers/UtRdKLoggerFactory.kt | 11 + .../{ProtocolRoot.kt => ChildProcessModel.kt} | 4 +- .../org/utbot/rd/models/EngineProcessModel.kt | 128 ++ .../org/utbot/rd/models/SettingsModel.kt | 18 + .../utbot/rd/models/SynchronizationModel.kt | 11 + 62 files changed, 3851 insertions(+), 1009 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/analytics/AnalyticsConfigureUtil.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/SignatureUtil.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt delete mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentationIO.kt rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/{ProtocolModel.Generated.kt => ChildProcessModel.Generated.kt} (93%) rename utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/{ProtocolRoot.Generated.kt => ChildProcessProtocolRoot.Generated.kt} (65%) create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt delete mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/UtRdKLogger.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModelRoot.Generated.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt rename utbot-rd/src/main/rdgen/org/utbot/rd/models/{ProtocolRoot.kt => ChildProcessModel.kt} (96%) create mode 100644 utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt create mode 100644 utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt create mode 100644 utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0bfba816d2..e654a026c9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -65,7 +65,7 @@ allprojects { override fun beforeSuite(suite: TestDescriptor) {} override fun beforeTest(testDescriptor: TestDescriptor) {} override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { - println("[$testDescriptor.classDisplayName] [$testDescriptor.displayName]: $result.resultType") + println("[$testDescriptor.classDisplayName] [$testDescriptor.displayName]: $result.resultType, length - ${(result.endTime - result.startTime) / 1000.0} sec") } override fun afterSuite(testDescriptor: TestDescriptor, result: TestResult) { diff --git a/gradle.properties b/gradle.properties index 3a1dc33f81..ae47d907d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,9 @@ kotlin.code.style=official # IU, IC, PC, PY, WS... ideType=IC -pythonCommunityPluginVersion=212.5457.59 +pythonCommunityPluginVersion=222.4167.37 #Version numbers: https://plugins.jetbrains.com/plugin/631-python/versions -pythonUltimatePluginVersion=212.5457.59 +pythonUltimatePluginVersion=222.4167.37 junit5Version=5.8.0-RC1 junit4Version=4.13.2 @@ -14,7 +14,7 @@ mockitoVersion=3.5.13 z3Version=4.8.9.1 z3JavaApiVersion=4.8.9 sootCommitHash=1f34746 -kotlinVersion=1.7.10 +kotlinVersion=1.7.20 log4j2Version=2.13.3 coroutinesVersion=1.6.3 collectionsVersion=0.3.4 diff --git a/utbot-core/src/main/kotlin/org/utbot/common/AbstractSettings.kt b/utbot-core/src/main/kotlin/org/utbot/common/AbstractSettings.kt index dea2b80b7a..f564d98a20 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/AbstractSettings.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/AbstractSettings.kt @@ -6,14 +6,33 @@ import java.util.* import mu.KLogger import org.utbot.common.PathUtil.toPath import kotlin.properties.PropertyDelegateProvider +import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -abstract class AbstractSettings( +interface SettingsContainer { + fun settingFor(defaultValue: T, converter: (String) -> T): PropertyDelegateProvider> +} + +interface SettingsContainerFactory { + fun createSettingsContainer( + logger: KLogger, + defaultKeyForSettingsPath: String, + defaultSettingsPath: String? = null) : SettingsContainer +} + +class PropertiesSettingsContainer( private val logger: KLogger, defaultKeyForSettingsPath: String, - defaultSettingsPath: String? = null -) { - protected val properties = Properties().also { props -> + defaultSettingsPath: String? = null): SettingsContainer { + companion object: SettingsContainerFactory { + override fun createSettingsContainer( + logger: KLogger, + defaultKeyForSettingsPath: String, + defaultSettingsPath: String? + ): SettingsContainer = PropertiesSettingsContainer(logger, defaultKeyForSettingsPath, defaultSettingsPath) + } + + private val properties = Properties().also { props -> val settingsPath = System.getProperty(defaultKeyForSettingsPath) ?: defaultSettingsPath val settingsPathFile = settingsPath?.toPath()?.toFile() if (settingsPathFile?.exists() == true) { @@ -27,18 +46,18 @@ abstract class AbstractSettings( } } - protected val settingsValues: MutableMap, Any?> = mutableMapOf() + private val settingsValues: MutableMap, Any?> = mutableMapOf() - inner class SettingDelegate(val property: KProperty<*>, val initializer: () -> T) { + inner class SettingDelegate(val property: KProperty<*>, val initializer: () -> T): ReadWriteProperty { private var value = initializer() init { updateSettingValue() } - operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = value updateSettingValue() } @@ -48,10 +67,10 @@ abstract class AbstractSettings( } } - protected fun getProperty( + override fun settingFor( defaultValue: T, converter: (String) -> T - ): PropertyDelegateProvider> { + ): PropertyDelegateProvider> { return PropertyDelegateProvider { _, property -> SettingDelegate(property) { try { @@ -64,6 +83,45 @@ abstract class AbstractSettings( } } + override fun toString(): String = + settingsValues + .mapKeys { it.key.name } + .entries + .sortedBy { it.key } + .joinToString(separator = System.lineSeparator()) { "\t${it.key}=${it.value}" } +} + +abstract class AbstractSettings( + logger: KLogger, + defaultKeyForSettingsPath: String, + defaultSettingsPath: String? = null) { + private val container: SettingsContainer = createSettingsContainer(logger, defaultKeyForSettingsPath, defaultSettingsPath) + init { + allSettings[defaultKeyForSettingsPath] = this + } + companion object : SettingsContainerFactory { + val allSettings = mutableMapOf() + private var factory: SettingsContainerFactory? = null + override fun createSettingsContainer( + logger: KLogger, + defaultKeyForSettingsPath: String, + defaultSettingsPath: String? + ): SettingsContainer { + return (factory ?: PropertiesSettingsContainer).createSettingsContainer(logger, defaultKeyForSettingsPath, defaultSettingsPath) + } + + fun setupFactory(factory: SettingsContainerFactory) { + this.factory = factory + } + } + + protected fun getProperty( + defaultValue: T, + converter: (String) -> T + ): PropertyDelegateProvider> { + return container.settingFor(defaultValue, converter) + } + protected fun getBooleanProperty(defaultValue: Boolean) = getProperty(defaultValue, String::toBoolean) protected fun getIntProperty(defaultValue: Int) = getProperty(defaultValue, String::toInt) protected fun getLongProperty(defaultValue: Long) = getProperty(defaultValue, String::toLong) @@ -71,10 +129,5 @@ abstract class AbstractSettings( protected inline fun > getEnumProperty(defaultValue: T) = getProperty(defaultValue) { enumValueOf(it) } - override fun toString(): String = - settingsValues - .mapKeys { it.key.name } - .entries - .sortedBy { it.key } - .joinToString(separator = System.lineSeparator()) { "\t${it.key}=${it.value}" } + override fun toString(): String = container.toString() } \ No newline at end of file diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 0f390b5dbd..1c0a88d02a 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -4,7 +4,7 @@ import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import kotlin.reflect.KClass -val Class<*>.packageName: String get() = `package`?.name?:"" +val Class<*>.nameOfPackage: String get() = `package`?.name?:"" fun Method.invokeCatching(obj: Any?, args: List) = try { Result.success(invoke(obj, *args.toTypedArray())) 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 eb6a5a7a98..fe94851557 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/Logging.kt @@ -1,7 +1,9 @@ package org.utbot.common import mu.KLogger +import java.time.format.DateTimeFormatter +val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") class LoggerWithLogMethod(val logger: KLogger, val logMethod: (() -> Any?) -> Unit) 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 aa9366296b..019dabd8c0 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 @@ -2,6 +2,7 @@ package org.utbot.framework import mu.KotlinLogging import org.utbot.common.AbstractSettings +import org.utbot.common.PropertiesSettingsContainer import kotlin.reflect.KProperty private val logger = KotlinLogging.logger {} @@ -17,30 +18,6 @@ internal val utbotHomePath = "${System.getProperty("user.home")}/.utbot" private val defaultSettingsPath = "$utbotHomePath/settings.properties" private const val defaultKeyForSettingsPath = "utbot.settings.path" -/** - * Stores current values for each setting from [UtSettings]. - */ -private val settingsValues: MutableMap, Any?> = mutableMapOf() - -internal class SettingDelegate(val property: KProperty<*>, val initializer: () -> T) { - private var value = initializer() - - init { - updateSettingValue() - } - - operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this.value = value - updateSettingValue() - } - - private fun updateSettingValue() { - settingsValues[property] = value - } -} - /** * Default concrete execution timeout (in milliseconds). */ @@ -282,7 +259,7 @@ object UtSettings : AbstractSettings( ) /** - * Determines whether should errors from a child process be written to a log file or suppressed. + * Determines whether should errors from a child process and idea engine 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). diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 00d9492950..dc704ea19d 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -33,19 +33,7 @@ import org.utbot.framework.plugin.api.util.shortClassId import org.utbot.framework.plugin.api.util.supertypeOfAnonymousClass import org.utbot.framework.plugin.api.util.toReferenceTypeBytecodeSignature import org.utbot.framework.plugin.api.util.voidClassId -import soot.ArrayType -import soot.BooleanType -import soot.ByteType -import soot.CharType -import soot.DoubleType -import soot.FloatType -import soot.IntType -import soot.LongType -import soot.RefType -import soot.ShortType -import soot.SootClass -import soot.Type -import soot.VoidType +import soot.* import soot.jimple.JimpleBody import soot.jimple.Stmt import java.io.File @@ -58,6 +46,7 @@ const val SYMBOLIC_NULL_ADDR: Int = 0 data class UtMethodTestSet( val method: ExecutableId, val executions: List = emptyList(), + // in idea process this property is null val jimpleBody: JimpleBody? = null, val errors: Map = emptyMap(), val clustersInfo: List> = listOf(null to executions.indices) diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt index 1b8eedaa93..7fd12c48b1 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.utbot.common.nameOfPackage import org.utbot.framework.plugin.services.JdkInfoDefaultProvider import org.utbot.framework.util.SootUtils diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index 7118531ee8..c22f996332 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -14,11 +14,15 @@ dependencies { api project(':utbot-instrumentation') api project(':utbot-summary') 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 "com.github.UnitTestBot:soot:${sootCommitHash}" + implementation group: 'com.esotericsoftware.kryo', name: 'kryo5', version: kryoVersion + // this is necessary for serialization of some collections + implementation group: 'de.javakaffee', name: 'kryo-serializers', version: kryoSerializersVersion implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: jacksonVersion implementation group: 'org.sosy-lab', name: 'javasmt-solver-z3', version: javasmtSolverZ3Version diff --git a/utbot-framework/src/main/kotlin/org/utbot/analytics/AnalyticsConfigureUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/analytics/AnalyticsConfigureUtil.kt new file mode 100644 index 0000000000..82c74e887f --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/analytics/AnalyticsConfigureUtil.kt @@ -0,0 +1,53 @@ +package org.utbot.analytics + +import mu.KotlinLogging +import org.utbot.framework.PathSelectorType +import org.utbot.framework.UtSettings + +private val logger = KotlinLogging.logger {} + +object AnalyticsConfigureUtil { + /** + * Configures utbot-analytics models for the better path selection. + * + * NOTE: If analytics configuration for the NN Path Selector could not be loaded, + * it switches to the [PathSelectorType.INHERITORS_SELECTOR]. + */ + fun configureML() { + logger.info { "PathSelectorType: ${UtSettings.pathSelectorType}" } + + if (UtSettings.pathSelectorType == PathSelectorType.ML_SELECTOR) { + val analyticsConfigurationClassPath = UtSettings.analyticsConfigurationClassPath + tryToSetUpMLSelector(analyticsConfigurationClassPath) + } + + if (UtSettings.pathSelectorType == PathSelectorType.TORCH_SELECTOR) { + val analyticsConfigurationClassPath = UtSettings.analyticsTorchConfigurationClassPath + tryToSetUpMLSelector(analyticsConfigurationClassPath) + } + } + + private fun tryToSetUpMLSelector(analyticsConfigurationClassPath: String) { + try { + Class.forName(analyticsConfigurationClassPath) + Predictors.stateRewardPredictor = EngineAnalyticsContext.mlPredictorFactory() + + logger.info { "RewardModelPath: ${UtSettings.modelPath}" } + } catch (e: ClassNotFoundException) { + logger.error { + "Configuration of the predictors from the utbot-analytics module described in the class: " + + "$analyticsConfigurationClassPath is not found!" + } + + logger.info(e) { + "Error while initialization of ${UtSettings.pathSelectorType}. Changing pathSelectorType on INHERITORS_SELECTOR" + } + UtSettings.pathSelectorType = PathSelectorType.INHERITORS_SELECTOR + } + catch (e: Exception) { // engine not found, for example + logger.error { e.message } + UtSettings.pathSelectorType = PathSelectorType.INHERITORS_SELECTOR + } + } + +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt index 809620dc10..63c32a753f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -1,7 +1,6 @@ package org.utbot.engine import org.utbot.api.mock.UtMock -import org.utbot.common.packageName import org.utbot.engine.overrides.UtArrayMock import org.utbot.engine.overrides.UtLogicMock import org.utbot.engine.overrides.UtOverrideMock @@ -18,6 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KFunction2 import kotlin.reflect.KFunction5 import kotlinx.collections.immutable.persistentListOf +import org.utbot.common.nameOfPackage import org.utbot.engine.util.mockListeners.MockListenerController import org.utbot.framework.util.isInaccessibleViaReflection import soot.BooleanType @@ -361,7 +361,7 @@ val assumeOrExecuteConcretelyBytecodeSignature: String val disableClassCastExceptionCheckBytecodeSignature: String get() = disableClassCastExceptionCheckMethod.executableId.signature -internal val UTBOT_OVERRIDE_PACKAGE_NAME = UtOverrideMock::class.java.packageName +internal val UTBOT_OVERRIDE_PACKAGE_NAME = UtOverrideMock::class.java.nameOfPackage private val arraycopyMethod : KFunction5, Int, Array, Int, Int, Unit> = UtArrayMock::arraycopy internal val utArrayMockArraycopyMethodName = arraycopyMethod.name diff --git a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt index 7cf9b8f3e5..4871fdfc84 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt @@ -1,6 +1,7 @@ package org.utbot.external.api import org.utbot.common.FileUtil +import org.utbot.common.nameOfPackage import org.utbot.framework.UtSettings import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.Junit5 @@ -57,7 +58,7 @@ object UtBotJavaApi { staticsMocking: StaticsMocking = NoStaticMocking, generateWarningsForStaticMocking: Boolean = false, forceStaticMocking: ForceStaticMocking = ForceStaticMocking.DO_NOT_FORCE, - testClassPackageName: String = classUnderTest.packageName + testClassPackageName: String = classUnderTest.nameOfPackage ): String { val utContext = UtContext(classUnderTest.classLoader) diff --git a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt index cc11e50b98..e7c403570c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt @@ -1,5 +1,6 @@ package org.utbot.external.api +import org.utbot.common.nameOfPackage import org.utbot.framework.assemble.AssembleModelGenerator import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId @@ -8,7 +9,6 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.id import java.lang.reflect.Field import java.lang.reflect.Method @@ -49,7 +49,7 @@ class UtModelFactory( classUnderTest: Class<*>, models: List ): IdentityHashMap = - AssembleModelGenerator(classUnderTest.packageName) + AssembleModelGenerator(classUnderTest.nameOfPackage) .createAssembleModels(models) @JvmOverloads diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt index 5947ec8674..a8e632976c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt @@ -121,7 +121,7 @@ data class CodeGeneratorResult( val generatedCode: String, // null if no util class needed, e.g. when we are generating utils directly into test class val utilClassKind: UtilClassKind?, - val testsGenerationReport: TestsGenerationReport, + val testsGenerationReport: TestsGenerationReport ) /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/SignatureUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/SignatureUtil.kt new file mode 100644 index 0000000000..d8825e22e4 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/SignatureUtil.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.plugin.api + +import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.jvm.javaType + +fun KFunction<*>.signature() = + Signature(this.name, this.parameters.filter { it.kind == KParameter.Kind.VALUE }.map { it.type.javaType.typeName }) + +data class Signature(val name: String, val parameterTypes: List) { + + fun normalized() = this.copy( + parameterTypes = parameterTypes.map { + it?.replace("$", ".") // normalize names of nested classes + } + ) +} \ No newline at end of file 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 new file mode 100644 index 0000000000..16c888ddda --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt @@ -0,0 +1,283 @@ +package org.utbot.framework.process + +import com.jetbrains.rd.framework.IProtocol +import com.jetbrains.rd.util.Logger +import com.jetbrains.rd.util.lifetime.Lifetime +import kotlinx.coroutines.runBlocking +import mu.KotlinLogging +import org.utbot.analytics.AnalyticsConfigureUtil +import org.utbot.common.AbstractSettings +import org.utbot.common.allNestedClasses +import org.utbot.common.appendHtmlLine +import org.utbot.common.nameOfPackage +import org.utbot.engine.util.mockListeners.ForceMockListener +import org.utbot.engine.util.mockListeners.ForceStaticMockListener +import org.utbot.framework.codegen.* +import org.utbot.framework.codegen.model.CodeGenerator +import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.Signature +import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.services.JdkInfo +import org.utbot.framework.process.generated.* +import org.utbot.framework.util.Conflict +import org.utbot.framework.util.ConflictTriggers +import org.utbot.instrumentation.util.KryoHelper +import org.utbot.rd.CallsSynchronizer +import org.utbot.rd.ClientProtocolBuilder +import org.utbot.rd.findRdPort +import org.utbot.rd.loggers.UtRdKLoggerFactory +import org.utbot.sarif.RdSourceFindingStrategyFacade +import org.utbot.sarif.SarifReport +import org.utbot.summary.summarize +import soot.SootMethod +import soot.UnitPatchingChain +import soot.util.HashChain +import java.io.File +import java.net.URLClassLoader +import java.nio.file.Paths +import java.util.* +import kotlin.reflect.full.functions +import kotlin.time.Duration.Companion.seconds + +private val messageFromMainTimeoutMillis = 120.seconds +private val logger = KotlinLogging.logger {} + +// use log4j2.configurationFile property to set log4j configuration +suspend fun main(args: Array) = runBlocking { + // 0 - auto port for server, should not be used here + val port = findRdPort(args) + + Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) + + ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { + settingsModel + rdSourceFindingStrategy + + AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol)) + val kryoHelper = KryoHelper(lifetime) + engineProcessModel.setup(kryoHelper, it, protocol) + } + logger.info { "runBlocking ending" } +}.also { + logger.info { "runBlocking ended" } +} + +private lateinit var testGenerator: TestCaseGenerator +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 +) { + val model = this + synchronizer.measureExecutionForTermination(setupUtContext) { params -> + UtContext.setUtContext(UtContext(URLClassLoader(params.classpathForUrlsClassloader.map { + File(it).toURI().toURL() + }.toTypedArray()))) + } + synchronizer.measureExecutionForTermination(createTestGenerator) { params -> + AnalyticsConfigureUtil.configureML() + testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, + classpath = params.classpath, + dependencyPaths = params.dependencyPaths, + jdkInfo = JdkInfo(Paths.get(params.jdkInfo.path), params.jdkInfo.version), + isCanceled = { + runBlocking { + model.isCancelled.startSuspending(Unit) + } + }) + } + synchronizer.measureExecutionForTermination(generate) { params -> + val mockFrameworkInstalled = params.mockInstalled + val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers)) + if (!mockFrameworkInstalled) { + ForceMockListener.create(testGenerator, conflictTriggers) + } + val staticsMockingConfigured = params.staticsMockingIsConfigureda + if (!staticsMockingConfigured) { + ForceStaticMockListener.create(testGenerator, conflictTriggers) + } + val result = testGenerator.generate(kryoHelper.readObject(params.methods), + MockStrategyApi.valueOf(params.mockStrategy), + kryoHelper.readObject(params.chosenClassesToMockAlways), + params.timeout, + generate = testFlow { + generationTimeout = params.generationTimeout + isSymbolicEngineEnabled = params.isSymbolicEngineEnabled + isFuzzingEnabled = params.isFuzzingEnabled + fuzzingValue = params.fuzzingValue + }) + .map { it.summarize(Paths.get(params.searchDirectory)) } + .filterNot { it.executions.isEmpty() && it.errors.isEmpty() } + + val id = ++idCounter + + testSets[id] = result + GenerateResult(result.size, id) + } + synchronizer.measureExecutionForTermination(render) { params -> + val testFramework = testFrameworkByName(params.testFramework) + val staticMocking = if (params.staticsMocking.startsWith("No")) { + NoStaticMocking + } else { + MockitoStaticMocking + } + val classId: ClassId = kryoHelper.readObject(params.classUnderTest) + val testSetsId: Long = params.testSetsId + val codeGenerator = CodeGenerator( + classUnderTest = classId, + generateUtilClassFile = params.generateUtilClassFile, + paramNames = kryoHelper.readObject(params.paramNames), + testFramework = testFramework, + mockFramework = MockFramework.valueOf(params.mockFramework), + codegenLanguage = CodegenLanguage.valueOf(params.codegenLanguage), + parameterizedTestSource = ParametrizedTestSource.valueOf(params.parameterizedTestSource), + staticsMocking = staticMocking, + forceStaticMocking = kryoHelper.readObject(params.forceStaticMocking), + generateWarningsForStaticMocking = params.generateWarningsForStaticMocking, + runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf(params.runtimeExceptionTestsBehaviour), + hangingTestsTimeout = HangingTestsTimeout(params.hangingTestsTimeout), + enableTestsTimeout = params.enableTestsTimeout, + testClassPackageName = params.testClassPackageName + ) + codeGenerator.generateAsStringWithTestReport(testSets[testSetsId]!!) + .let { + testGenerationReports.add(it.testsGenerationReport) + RenderResult(it.generatedCode, kryoHelper.writeObject(it.utilClassKind)) + } + } + synchronizer.measureExecutionForTermination(stopProcess) { synchronizer.stopProtocol() } + synchronizer.measureExecutionForTermination(obtainClassId) { canonicalName -> + kryoHelper.writeObject(UtContext.currentContext()!!.classLoader.loadClass(canonicalName).id) + } + synchronizer.measureExecutionForTermination(findMethodsInClassMatchingSelected) { params -> + val classId = kryoHelper.readObject(params.classId) + val selectedSignatures = params.signatures.map { Signature(it.name, it.parametersTypes) } + FindMethodsInClassMatchingSelectedResult(kryoHelper.writeObject(classId.jClass.kotlin.allNestedClasses.flatMap { clazz -> + clazz.functions.sortedWith(compareBy { selectedSignatures.indexOf(it.signature()) }) + .filter { it.signature().normalized() in selectedSignatures } + .map { it.executableId } + })) + } + synchronizer.measureExecutionForTermination(findMethodParamNames) { params -> + val classId = kryoHelper.readObject(params.classId) + val bySignature = kryoHelper.readObject>>(params.bySignature) + FindMethodParamNamesResult(kryoHelper.writeObject( + classId.jClass.kotlin.allNestedClasses.flatMap { it.functions } + .mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } } + .toMap() + )) + } + synchronizer.measureExecutionForTermination(writeSarifReport) { params -> + val reportFilePath = Paths.get(params.reportFilePath) + reportFilePath.toFile().writeText( + SarifReport( + testSets[params.testSetsId]!!, + params.generatedTestsCode, + RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy) + ).createReport() + ) + } + synchronizer.measureExecutionForTermination(generateTestReport) { params -> + val eventLogMessage = params.eventLogMessage + val testPackageName: String? = params.testPackageName + var hasWarnings = false + val reports = testGenerationReports + val isMultiPackage = params.isMultiPackage + val (notifyMessage, statistics) = if (reports.size == 1) { + val report = reports.first() + processInitialWarnings(report, params) + + val message = buildString { + appendHtmlLine(report.toString(isShort = true)) + + val classUnderTestPackageName = + report.classUnderTest.java.nameOfPackage + + destinationWarningMessage(testPackageName, classUnderTestPackageName) + ?.let { + hasWarnings = true + appendHtmlLine(it) + appendHtmlLine() + } + eventLogMessage?.let { + appendHtmlLine(it) + } + } + hasWarnings = hasWarnings || report.hasWarnings + Pair(message, report.detailedStatistics) + } else { + val accumulatedReport = reports.first() + processInitialWarnings(accumulatedReport, params) + + val message = buildString { + appendHtmlLine("${reports.sumBy { it.executables.size }} tests generated for ${reports.size} classes.") + + if (accumulatedReport.initialWarnings.isNotEmpty()) { + accumulatedReport.initialWarnings.forEach { appendHtmlLine(it()) } + appendHtmlLine() + } + + // TODO maybe add statistics info here + + for (report in reports) { + val classUnderTestPackageName = + report.classUnderTest.java.nameOfPackage + + hasWarnings = hasWarnings || report.hasWarnings + if (!isMultiPackage) { + val destinationWarning = + destinationWarningMessage(testPackageName, classUnderTestPackageName) + if (destinationWarning != null) { + hasWarnings = true + appendHtmlLine(destinationWarning) + appendHtmlLine() + } + } + } + eventLogMessage?.let { + appendHtmlLine(it) + } + } + + Pair(message, null) + } + GenerateTestReportResult(notifyMessage, statistics, hasWarnings) + } +} + +private fun processInitialWarnings(report: TestsGenerationReport, params: GenerateTestReportArgs) { + val hasInitialWarnings = params.hasInitialWarnings + + if (!hasInitialWarnings) { + return + } + + report.apply { + params.forceMockWarning?.let { + initialWarnings.add { it } + } + params.forceStaticMockWarnings?.let { + initialWarnings.add { it } + } + params.testFrameworkWarning?.let { + initialWarnings.add { it } + } + } +} + +private fun destinationWarningMessage(testPackageName: String?, classUnderTestPackageName: String): String? { + return if (classUnderTestPackageName != testPackageName) { + """ + Warning: Destination package $testPackageName does not match package of the class $classUnderTestPackageName. + This may cause unnecessary usage of reflection for protected or package-private fields and methods access. + """.trimIndent() + } else { + null + } +} \ 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 new file mode 100644 index 0000000000..4566dba88c --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdSettingsContainer.kt @@ -0,0 +1,44 @@ +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 kotlin.properties.PropertyDelegateProvider +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +class RdSettingsContainerFactory(private val protocol: IProtocol) : SettingsContainerFactory { + override fun createSettingsContainer( + logger: KLogger, + defaultKeyForSettingsPath: String, + defaultSettingsPath: String? + ): SettingsContainer { + return RdSettingsContainer(logger, defaultKeyForSettingsPath, protocol) + } +} + +class RdSettingsContainer(val logger: KLogger, val key: String, val protocol: IProtocol): 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 { + converter(it) + } ?: defaultValue + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + throw NotImplementedError("Setting properties from child process not supported") + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..72ba022123 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -0,0 +1,1298 @@ +@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 + +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 [EngineProcessModel.kt:20] + */ +class EngineProcessModel private constructor( + private val _setupUtContext: RdCall, + private val _createTestGenerator: RdCall, + private val _isCancelled: RdCall, + private val _generate: RdCall, + private val _render: RdCall, + private val _stopProcess: RdCall, + private val _obtainClassId: RdCall, + private val _findMethodsInClassMatchingSelected: RdCall, + private val _findMethodParamNames: RdCall, + private val _writeSarifReport: RdCall, + private val _generateTestReport: RdCall +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + serializers.register(JdkInfo) + serializers.register(TestGeneratorParams) + serializers.register(GenerateParams) + serializers.register(GenerateResult) + serializers.register(RenderParams) + serializers.register(RenderResult) + serializers.register(SetupContextParams) + serializers.register(Signature) + serializers.register(FindMethodsInClassMatchingSelectedArguments) + serializers.register(FindMethodsInClassMatchingSelectedResult) + serializers.register(FindMethodParamNamesArguments) + serializers.register(FindMethodParamNamesResult) + serializers.register(WriteSarifReportArguments) + serializers.register(GenerateTestReportArgs) + serializers.register(GenerateTestReportResult) + } + + + @JvmStatic + @JvmName("internalCreateModel") + @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): EngineProcessModel { + @Suppress("DEPRECATION") + return create(lifetime, protocol) + } + + @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) + + return EngineProcessModel().apply { + identify(protocol.identity, RdId.Null.mix("EngineProcessModel")) + bind(lifetime, protocol, "EngineProcessModel") + } + } + + + const val serializationHash = 4674749231408610997L + + } + override val serializersOwner: ISerializersOwner get() = EngineProcessModel + override val serializationHash: Long get() = EngineProcessModel.serializationHash + + //fields + val setupUtContext: RdCall get() = _setupUtContext + val createTestGenerator: RdCall get() = _createTestGenerator + val isCancelled: RdCall get() = _isCancelled + val generate: RdCall get() = _generate + val render: RdCall get() = _render + val stopProcess: RdCall get() = _stopProcess + val obtainClassId: RdCall get() = _obtainClassId + val findMethodsInClassMatchingSelected: RdCall get() = _findMethodsInClassMatchingSelected + val findMethodParamNames: RdCall get() = _findMethodParamNames + val writeSarifReport: RdCall get() = _writeSarifReport + val generateTestReport: RdCall get() = _generateTestReport + //methods + //initializer + init { + _setupUtContext.async = true + _createTestGenerator.async = true + _isCancelled.async = true + _generate.async = true + _render.async = true + _stopProcess.async = true + _obtainClassId.async = true + _findMethodsInClassMatchingSelected.async = true + _findMethodParamNames.async = true + _writeSarifReport.async = true + _generateTestReport.async = true + } + + init { + bindableChildren.add("setupUtContext" to _setupUtContext) + bindableChildren.add("createTestGenerator" to _createTestGenerator) + bindableChildren.add("isCancelled" to _isCancelled) + bindableChildren.add("generate" to _generate) + bindableChildren.add("render" to _render) + bindableChildren.add("stopProcess" to _stopProcess) + bindableChildren.add("obtainClassId" to _obtainClassId) + bindableChildren.add("findMethodsInClassMatchingSelected" to _findMethodsInClassMatchingSelected) + bindableChildren.add("findMethodParamNames" to _findMethodParamNames) + bindableChildren.add("writeSarifReport" to _writeSarifReport) + bindableChildren.add("generateTestReport" to _generateTestReport) + } + + //secondary constructor + private constructor( + ) : this( + RdCall(SetupContextParams, FrameworkMarshallers.Void), + RdCall(TestGeneratorParams, FrameworkMarshallers.Void), + RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Bool), + RdCall(GenerateParams, GenerateResult), + RdCall(RenderParams, RenderResult), + RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void), + RdCall(FrameworkMarshallers.String, FrameworkMarshallers.ByteArray), + RdCall(FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult), + RdCall(FindMethodParamNamesArguments, FindMethodParamNamesResult), + RdCall(WriteSarifReportArguments, FrameworkMarshallers.Void), + RdCall(GenerateTestReportArgs, GenerateTestReportResult) + ) + + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("EngineProcessModel (") + printer.indent { + print("setupUtContext = "); _setupUtContext.print(printer); println() + print("createTestGenerator = "); _createTestGenerator.print(printer); println() + print("isCancelled = "); _isCancelled.print(printer); println() + print("generate = "); _generate.print(printer); println() + print("render = "); _render.print(printer); println() + print("stopProcess = "); _stopProcess.print(printer); println() + print("obtainClassId = "); _obtainClassId.print(printer); println() + print("findMethodsInClassMatchingSelected = "); _findMethodsInClassMatchingSelected.print(printer); println() + print("findMethodParamNames = "); _findMethodParamNames.print(printer); println() + print("writeSarifReport = "); _writeSarifReport.print(printer); println() + print("generateTestReport = "); _generateTestReport.print(printer); println() + } + printer.print(")") + } + //deepClone + override fun deepClone(): EngineProcessModel { + return EngineProcessModel( + _setupUtContext.deepClonePolymorphic(), + _createTestGenerator.deepClonePolymorphic(), + _isCancelled.deepClonePolymorphic(), + _generate.deepClonePolymorphic(), + _render.deepClonePolymorphic(), + _stopProcess.deepClonePolymorphic(), + _obtainClassId.deepClonePolymorphic(), + _findMethodsInClassMatchingSelected.deepClonePolymorphic(), + _findMethodParamNames.deepClonePolymorphic(), + _writeSarifReport.deepClonePolymorphic(), + _generateTestReport.deepClonePolymorphic() + ) + } + //contexts +} +val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel::class) { @Suppress("DEPRECATION") EngineProcessModel.create(lifetime, this) } + + + +/** + * #### Generated from [EngineProcessModel.kt:89] + */ +data class FindMethodParamNamesArguments ( + val classId: ByteArray, + val bySignature: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = FindMethodParamNamesArguments::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodParamNamesArguments { + val classId = buffer.readByteArray() + val bySignature = buffer.readByteArray() + return FindMethodParamNamesArguments(classId, bySignature) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: FindMethodParamNamesArguments) { + buffer.writeByteArray(value.classId) + buffer.writeByteArray(value.bySignature) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as FindMethodParamNamesArguments + + if (!(classId contentEquals other.classId)) return false + if (!(bySignature contentEquals other.bySignature)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + classId.contentHashCode() + __r = __r*31 + bySignature.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("FindMethodParamNamesArguments (") + printer.indent { + print("classId = "); classId.print(printer); println() + print("bySignature = "); bySignature.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:93] + */ +data class FindMethodParamNamesResult ( + val paramNames: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = FindMethodParamNamesResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodParamNamesResult { + val paramNames = buffer.readByteArray() + return FindMethodParamNamesResult(paramNames) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: FindMethodParamNamesResult) { + buffer.writeByteArray(value.paramNames) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as FindMethodParamNamesResult + + if (!(paramNames contentEquals other.paramNames)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + paramNames.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("FindMethodParamNamesResult (") + printer.indent { + print("paramNames = "); paramNames.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:82] + */ +data class FindMethodsInClassMatchingSelectedArguments ( + val classId: ByteArray, + val signatures: List +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = FindMethodsInClassMatchingSelectedArguments::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodsInClassMatchingSelectedArguments { + val classId = buffer.readByteArray() + val signatures = buffer.readList { Signature.read(ctx, buffer) } + return FindMethodsInClassMatchingSelectedArguments(classId, signatures) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: FindMethodsInClassMatchingSelectedArguments) { + buffer.writeByteArray(value.classId) + buffer.writeList(value.signatures) { v -> Signature.write(ctx, buffer, v) } + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as FindMethodsInClassMatchingSelectedArguments + + if (!(classId contentEquals other.classId)) return false + if (signatures != other.signatures) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + classId.contentHashCode() + __r = __r*31 + signatures.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("FindMethodsInClassMatchingSelectedArguments (") + printer.indent { + print("classId = "); classId.print(printer); println() + print("signatures = "); signatures.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:86] + */ +data class FindMethodsInClassMatchingSelectedResult ( + val executableIds: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = FindMethodsInClassMatchingSelectedResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): FindMethodsInClassMatchingSelectedResult { + val executableIds = buffer.readByteArray() + return FindMethodsInClassMatchingSelectedResult(executableIds) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: FindMethodsInClassMatchingSelectedResult) { + buffer.writeByteArray(value.executableIds) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as FindMethodsInClassMatchingSelectedResult + + if (!(executableIds contentEquals other.executableIds)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + executableIds.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("FindMethodsInClassMatchingSelectedResult (") + printer.indent { + print("executableIds = "); executableIds.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:32] + */ +data class GenerateParams ( + val mockInstalled: Boolean, + val staticsMockingIsConfigureda: Boolean, + val conflictTriggers: ByteArray, + val methods: ByteArray, + val mockStrategy: String, + val chosenClassesToMockAlways: ByteArray, + val timeout: Long, + val generationTimeout: Long, + val isSymbolicEngineEnabled: Boolean, + val isFuzzingEnabled: Boolean, + val fuzzingValue: Double, + val searchDirectory: String +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GenerateParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GenerateParams { + val mockInstalled = buffer.readBool() + val staticsMockingIsConfigureda = buffer.readBool() + val conflictTriggers = buffer.readByteArray() + val methods = buffer.readByteArray() + val mockStrategy = buffer.readString() + val chosenClassesToMockAlways = buffer.readByteArray() + val timeout = buffer.readLong() + val generationTimeout = buffer.readLong() + val isSymbolicEngineEnabled = buffer.readBool() + val isFuzzingEnabled = buffer.readBool() + val fuzzingValue = buffer.readDouble() + val searchDirectory = buffer.readString() + return GenerateParams(mockInstalled, staticsMockingIsConfigureda, conflictTriggers, methods, mockStrategy, chosenClassesToMockAlways, timeout, generationTimeout, isSymbolicEngineEnabled, isFuzzingEnabled, fuzzingValue, searchDirectory) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateParams) { + buffer.writeBool(value.mockInstalled) + buffer.writeBool(value.staticsMockingIsConfigureda) + buffer.writeByteArray(value.conflictTriggers) + buffer.writeByteArray(value.methods) + buffer.writeString(value.mockStrategy) + buffer.writeByteArray(value.chosenClassesToMockAlways) + buffer.writeLong(value.timeout) + buffer.writeLong(value.generationTimeout) + buffer.writeBool(value.isSymbolicEngineEnabled) + buffer.writeBool(value.isFuzzingEnabled) + buffer.writeDouble(value.fuzzingValue) + buffer.writeString(value.searchDirectory) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GenerateParams + + if (mockInstalled != other.mockInstalled) return false + if (staticsMockingIsConfigureda != other.staticsMockingIsConfigureda) return false + if (!(conflictTriggers contentEquals other.conflictTriggers)) return false + if (!(methods contentEquals other.methods)) return false + if (mockStrategy != other.mockStrategy) return false + if (!(chosenClassesToMockAlways contentEquals other.chosenClassesToMockAlways)) return false + if (timeout != other.timeout) return false + if (generationTimeout != other.generationTimeout) return false + if (isSymbolicEngineEnabled != other.isSymbolicEngineEnabled) return false + if (isFuzzingEnabled != other.isFuzzingEnabled) return false + if (fuzzingValue != other.fuzzingValue) return false + if (searchDirectory != other.searchDirectory) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + mockInstalled.hashCode() + __r = __r*31 + staticsMockingIsConfigureda.hashCode() + __r = __r*31 + conflictTriggers.contentHashCode() + __r = __r*31 + methods.contentHashCode() + __r = __r*31 + mockStrategy.hashCode() + __r = __r*31 + chosenClassesToMockAlways.contentHashCode() + __r = __r*31 + timeout.hashCode() + __r = __r*31 + generationTimeout.hashCode() + __r = __r*31 + isSymbolicEngineEnabled.hashCode() + __r = __r*31 + isFuzzingEnabled.hashCode() + __r = __r*31 + fuzzingValue.hashCode() + __r = __r*31 + searchDirectory.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GenerateParams (") + printer.indent { + print("mockInstalled = "); mockInstalled.print(printer); println() + print("staticsMockingIsConfigureda = "); staticsMockingIsConfigureda.print(printer); println() + print("conflictTriggers = "); conflictTriggers.print(printer); println() + print("methods = "); methods.print(printer); println() + print("mockStrategy = "); mockStrategy.print(printer); println() + print("chosenClassesToMockAlways = "); chosenClassesToMockAlways.print(printer); println() + print("timeout = "); timeout.print(printer); println() + print("generationTimeout = "); generationTimeout.print(printer); println() + print("isSymbolicEngineEnabled = "); isSymbolicEngineEnabled.print(printer); println() + print("isFuzzingEnabled = "); isFuzzingEnabled.print(printer); println() + print("fuzzingValue = "); fuzzingValue.print(printer); println() + print("searchDirectory = "); searchDirectory.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:50] + */ +data class GenerateResult ( + val notEmptyCases: Int, + val testSetsId: Long +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GenerateResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GenerateResult { + val notEmptyCases = buffer.readInt() + val testSetsId = buffer.readLong() + return GenerateResult(notEmptyCases, testSetsId) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateResult) { + buffer.writeInt(value.notEmptyCases) + buffer.writeLong(value.testSetsId) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GenerateResult + + if (notEmptyCases != other.notEmptyCases) return false + if (testSetsId != other.testSetsId) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + notEmptyCases.hashCode() + __r = __r*31 + testSetsId.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GenerateResult (") + printer.indent { + print("notEmptyCases = "); notEmptyCases.print(printer); println() + print("testSetsId = "); testSetsId.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:101] + */ +data class GenerateTestReportArgs ( + val eventLogMessage: String?, + val testPackageName: String?, + val isMultiPackage: Boolean, + val forceMockWarning: String?, + val forceStaticMockWarnings: String?, + val testFrameworkWarning: String?, + val hasInitialWarnings: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GenerateTestReportArgs::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GenerateTestReportArgs { + val eventLogMessage = buffer.readNullable { buffer.readString() } + val testPackageName = buffer.readNullable { buffer.readString() } + val isMultiPackage = buffer.readBool() + val forceMockWarning = buffer.readNullable { buffer.readString() } + val forceStaticMockWarnings = buffer.readNullable { buffer.readString() } + val testFrameworkWarning = buffer.readNullable { buffer.readString() } + val hasInitialWarnings = buffer.readBool() + return GenerateTestReportArgs(eventLogMessage, testPackageName, isMultiPackage, forceMockWarning, forceStaticMockWarnings, testFrameworkWarning, hasInitialWarnings) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateTestReportArgs) { + buffer.writeNullable(value.eventLogMessage) { buffer.writeString(it) } + buffer.writeNullable(value.testPackageName) { buffer.writeString(it) } + buffer.writeBool(value.isMultiPackage) + buffer.writeNullable(value.forceMockWarning) { buffer.writeString(it) } + buffer.writeNullable(value.forceStaticMockWarnings) { buffer.writeString(it) } + buffer.writeNullable(value.testFrameworkWarning) { buffer.writeString(it) } + buffer.writeBool(value.hasInitialWarnings) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GenerateTestReportArgs + + if (eventLogMessage != other.eventLogMessage) return false + if (testPackageName != other.testPackageName) return false + if (isMultiPackage != other.isMultiPackage) return false + if (forceMockWarning != other.forceMockWarning) return false + if (forceStaticMockWarnings != other.forceStaticMockWarnings) return false + if (testFrameworkWarning != other.testFrameworkWarning) return false + if (hasInitialWarnings != other.hasInitialWarnings) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + if (eventLogMessage != null) eventLogMessage.hashCode() else 0 + __r = __r*31 + if (testPackageName != null) testPackageName.hashCode() else 0 + __r = __r*31 + isMultiPackage.hashCode() + __r = __r*31 + if (forceMockWarning != null) forceMockWarning.hashCode() else 0 + __r = __r*31 + if (forceStaticMockWarnings != null) forceStaticMockWarnings.hashCode() else 0 + __r = __r*31 + if (testFrameworkWarning != null) testFrameworkWarning.hashCode() else 0 + __r = __r*31 + hasInitialWarnings.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GenerateTestReportArgs (") + printer.indent { + print("eventLogMessage = "); eventLogMessage.print(printer); println() + print("testPackageName = "); testPackageName.print(printer); println() + print("isMultiPackage = "); isMultiPackage.print(printer); println() + print("forceMockWarning = "); forceMockWarning.print(printer); println() + print("forceStaticMockWarnings = "); forceStaticMockWarnings.print(printer); println() + print("testFrameworkWarning = "); testFrameworkWarning.print(printer); println() + print("hasInitialWarnings = "); hasInitialWarnings.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:110] + */ +data class GenerateTestReportResult ( + val notifyMessage: String, + val statistics: String?, + val hasWarnings: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GenerateTestReportResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GenerateTestReportResult { + val notifyMessage = buffer.readString() + val statistics = buffer.readNullable { buffer.readString() } + val hasWarnings = buffer.readBool() + return GenerateTestReportResult(notifyMessage, statistics, hasWarnings) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateTestReportResult) { + buffer.writeString(value.notifyMessage) + buffer.writeNullable(value.statistics) { buffer.writeString(it) } + buffer.writeBool(value.hasWarnings) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GenerateTestReportResult + + if (notifyMessage != other.notifyMessage) return false + if (statistics != other.statistics) return false + if (hasWarnings != other.hasWarnings) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + notifyMessage.hashCode() + __r = __r*31 + if (statistics != null) statistics.hashCode() else 0 + __r = __r*31 + hasWarnings.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GenerateTestReportResult (") + printer.indent { + print("notifyMessage = "); notifyMessage.print(printer); println() + print("statistics = "); statistics.print(printer); println() + print("hasWarnings = "); hasWarnings.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:21] + */ +data class JdkInfo ( + val path: String, + val version: Int +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = JdkInfo::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): JdkInfo { + val path = buffer.readString() + val version = buffer.readInt() + return JdkInfo(path, version) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: JdkInfo) { + buffer.writeString(value.path) + buffer.writeInt(value.version) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as JdkInfo + + if (path != other.path) return false + if (version != other.version) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + path.hashCode() + __r = __r*31 + version.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("JdkInfo (") + printer.indent { + print("path = "); path.print(printer); println() + print("version = "); version.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:54] + */ +data class RenderParams ( + val testSetsId: Long, + val classUnderTest: ByteArray, + val paramNames: ByteArray, + val generateUtilClassFile: Boolean, + val testFramework: String, + val mockFramework: String, + val codegenLanguage: String, + val parameterizedTestSource: String, + val staticsMocking: String, + val forceStaticMocking: ByteArray, + val generateWarningsForStaticMocking: Boolean, + val runtimeExceptionTestsBehaviour: String, + val hangingTestsTimeout: Long, + val enableTestsTimeout: Boolean, + val testClassPackageName: String +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = RenderParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): RenderParams { + val testSetsId = buffer.readLong() + val classUnderTest = buffer.readByteArray() + val paramNames = buffer.readByteArray() + val generateUtilClassFile = buffer.readBool() + val testFramework = buffer.readString() + val mockFramework = buffer.readString() + val codegenLanguage = buffer.readString() + val parameterizedTestSource = buffer.readString() + val staticsMocking = buffer.readString() + val forceStaticMocking = buffer.readByteArray() + val generateWarningsForStaticMocking = buffer.readBool() + val runtimeExceptionTestsBehaviour = buffer.readString() + val hangingTestsTimeout = buffer.readLong() + val enableTestsTimeout = buffer.readBool() + val testClassPackageName = buffer.readString() + return RenderParams(testSetsId, classUnderTest, paramNames, generateUtilClassFile, testFramework, mockFramework, codegenLanguage, parameterizedTestSource, staticsMocking, forceStaticMocking, generateWarningsForStaticMocking, runtimeExceptionTestsBehaviour, hangingTestsTimeout, enableTestsTimeout, testClassPackageName) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: RenderParams) { + buffer.writeLong(value.testSetsId) + buffer.writeByteArray(value.classUnderTest) + buffer.writeByteArray(value.paramNames) + buffer.writeBool(value.generateUtilClassFile) + buffer.writeString(value.testFramework) + buffer.writeString(value.mockFramework) + buffer.writeString(value.codegenLanguage) + buffer.writeString(value.parameterizedTestSource) + buffer.writeString(value.staticsMocking) + buffer.writeByteArray(value.forceStaticMocking) + buffer.writeBool(value.generateWarningsForStaticMocking) + buffer.writeString(value.runtimeExceptionTestsBehaviour) + buffer.writeLong(value.hangingTestsTimeout) + buffer.writeBool(value.enableTestsTimeout) + buffer.writeString(value.testClassPackageName) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as RenderParams + + if (testSetsId != other.testSetsId) return false + if (!(classUnderTest contentEquals other.classUnderTest)) return false + if (!(paramNames contentEquals other.paramNames)) return false + if (generateUtilClassFile != other.generateUtilClassFile) return false + if (testFramework != other.testFramework) return false + if (mockFramework != other.mockFramework) return false + if (codegenLanguage != other.codegenLanguage) return false + if (parameterizedTestSource != other.parameterizedTestSource) return false + if (staticsMocking != other.staticsMocking) return false + if (!(forceStaticMocking contentEquals other.forceStaticMocking)) return false + if (generateWarningsForStaticMocking != other.generateWarningsForStaticMocking) return false + if (runtimeExceptionTestsBehaviour != other.runtimeExceptionTestsBehaviour) return false + if (hangingTestsTimeout != other.hangingTestsTimeout) return false + if (enableTestsTimeout != other.enableTestsTimeout) return false + if (testClassPackageName != other.testClassPackageName) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + testSetsId.hashCode() + __r = __r*31 + classUnderTest.contentHashCode() + __r = __r*31 + paramNames.contentHashCode() + __r = __r*31 + generateUtilClassFile.hashCode() + __r = __r*31 + testFramework.hashCode() + __r = __r*31 + mockFramework.hashCode() + __r = __r*31 + codegenLanguage.hashCode() + __r = __r*31 + parameterizedTestSource.hashCode() + __r = __r*31 + staticsMocking.hashCode() + __r = __r*31 + forceStaticMocking.contentHashCode() + __r = __r*31 + generateWarningsForStaticMocking.hashCode() + __r = __r*31 + runtimeExceptionTestsBehaviour.hashCode() + __r = __r*31 + hangingTestsTimeout.hashCode() + __r = __r*31 + enableTestsTimeout.hashCode() + __r = __r*31 + testClassPackageName.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("RenderParams (") + printer.indent { + print("testSetsId = "); testSetsId.print(printer); println() + print("classUnderTest = "); classUnderTest.print(printer); println() + print("paramNames = "); paramNames.print(printer); println() + print("generateUtilClassFile = "); generateUtilClassFile.print(printer); println() + print("testFramework = "); testFramework.print(printer); println() + print("mockFramework = "); mockFramework.print(printer); println() + print("codegenLanguage = "); codegenLanguage.print(printer); println() + print("parameterizedTestSource = "); parameterizedTestSource.print(printer); println() + print("staticsMocking = "); staticsMocking.print(printer); println() + print("forceStaticMocking = "); forceStaticMocking.print(printer); println() + print("generateWarningsForStaticMocking = "); generateWarningsForStaticMocking.print(printer); println() + print("runtimeExceptionTestsBehaviour = "); runtimeExceptionTestsBehaviour.print(printer); println() + print("hangingTestsTimeout = "); hangingTestsTimeout.print(printer); println() + print("enableTestsTimeout = "); enableTestsTimeout.print(printer); println() + print("testClassPackageName = "); testClassPackageName.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:71] + */ +data class RenderResult ( + val generatedCode: String, + val utilClassKind: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = RenderResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): RenderResult { + val generatedCode = buffer.readString() + val utilClassKind = buffer.readByteArray() + return RenderResult(generatedCode, utilClassKind) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: RenderResult) { + buffer.writeString(value.generatedCode) + buffer.writeByteArray(value.utilClassKind) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as RenderResult + + if (generatedCode != other.generatedCode) return false + if (!(utilClassKind contentEquals other.utilClassKind)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + generatedCode.hashCode() + __r = __r*31 + utilClassKind.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("RenderResult (") + printer.indent { + print("generatedCode = "); generatedCode.print(printer); println() + print("utilClassKind = "); utilClassKind.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:75] + */ +data class SetupContextParams ( + val classpathForUrlsClassloader: List +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SetupContextParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SetupContextParams { + val classpathForUrlsClassloader = buffer.readList { buffer.readString() } + return SetupContextParams(classpathForUrlsClassloader) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SetupContextParams) { + buffer.writeList(value.classpathForUrlsClassloader) { v -> buffer.writeString(v) } + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SetupContextParams + + if (classpathForUrlsClassloader != other.classpathForUrlsClassloader) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + classpathForUrlsClassloader.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SetupContextParams (") + printer.indent { + print("classpathForUrlsClassloader = "); classpathForUrlsClassloader.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:78] + */ +data class Signature ( + val name: String, + val parametersTypes: List +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = Signature::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): Signature { + val name = buffer.readString() + val parametersTypes = buffer.readList { buffer.readNullable { buffer.readString() } } + return Signature(name, parametersTypes) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: Signature) { + buffer.writeString(value.name) + buffer.writeList(value.parametersTypes) { v -> buffer.writeNullable(v) { buffer.writeString(it) } } + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as Signature + + if (name != other.name) return false + if (parametersTypes != other.parametersTypes) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + name.hashCode() + __r = __r*31 + parametersTypes.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("Signature (") + printer.indent { + print("name = "); name.print(printer); println() + print("parametersTypes = "); parametersTypes.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:26] + */ +data class TestGeneratorParams ( + val buildDir: Array, + val classpath: String?, + val dependencyPaths: String, + val jdkInfo: JdkInfo +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = TestGeneratorParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestGeneratorParams { + val buildDir = buffer.readArray {buffer.readString()} + val classpath = buffer.readNullable { buffer.readString() } + val dependencyPaths = buffer.readString() + val jdkInfo = JdkInfo.read(ctx, buffer) + return TestGeneratorParams(buildDir, classpath, dependencyPaths, jdkInfo) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestGeneratorParams) { + buffer.writeArray(value.buildDir) { buffer.writeString(it) } + buffer.writeNullable(value.classpath) { buffer.writeString(it) } + buffer.writeString(value.dependencyPaths) + JdkInfo.write(ctx, buffer, value.jdkInfo) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as TestGeneratorParams + + if (!(buildDir contentDeepEquals other.buildDir)) return false + if (classpath != other.classpath) return false + if (dependencyPaths != other.dependencyPaths) return false + if (jdkInfo != other.jdkInfo) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + buildDir.contentDeepHashCode() + __r = __r*31 + if (classpath != null) classpath.hashCode() else 0 + __r = __r*31 + dependencyPaths.hashCode() + __r = __r*31 + jdkInfo.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("TestGeneratorParams (") + printer.indent { + print("buildDir = "); buildDir.print(printer); println() + print("classpath = "); classpath.print(printer); println() + print("dependencyPaths = "); dependencyPaths.print(printer); println() + print("jdkInfo = "); jdkInfo.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [EngineProcessModel.kt:96] + */ +data class WriteSarifReportArguments ( + val testSetsId: Long, + val reportFilePath: String, + val generatedTestsCode: String +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = WriteSarifReportArguments::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): WriteSarifReportArguments { + val testSetsId = buffer.readLong() + val reportFilePath = buffer.readString() + val generatedTestsCode = buffer.readString() + return WriteSarifReportArguments(testSetsId, reportFilePath, generatedTestsCode) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: WriteSarifReportArguments) { + buffer.writeLong(value.testSetsId) + buffer.writeString(value.reportFilePath) + buffer.writeString(value.generatedTestsCode) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as WriteSarifReportArguments + + if (testSetsId != other.testSetsId) return false + if (reportFilePath != other.reportFilePath) return false + if (generatedTestsCode != other.generatedTestsCode) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + testSetsId.hashCode() + __r = __r*31 + reportFilePath.hashCode() + __r = __r*31 + generatedTestsCode.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("WriteSarifReportArguments (") + printer.indent { + print("testSetsId = "); testSetsId.print(printer); println() + print("reportFilePath = "); reportFilePath.print(printer); println() + print("generatedTestsCode = "); generatedTestsCode.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} 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/EngineProcessProtocolRoot.Generated.kt new file mode 100644 index 0000000000..dc9c7ce86b --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt @@ -0,0 +1,59 @@ +@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 + +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 [EngineProcessModel.kt:5] + */ +class EngineProcessProtocolRoot private constructor( +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + EngineProcessProtocolRoot.register(serializers) + EngineProcessModel.register(serializers) + RdSourceFindingStrategy.register(serializers) + } + + + + + + const val serializationHash = -4532543668004925627L + + } + override val serializersOwner: ISerializersOwner get() = EngineProcessProtocolRoot + override val serializationHash: Long get() = EngineProcessProtocolRoot.serializationHash + + //fields + //methods + //initializer + //secondary constructor + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("EngineProcessProtocolRoot (") + printer.print(")") + } + //deepClone + override fun deepClone(): EngineProcessProtocolRoot { + return EngineProcessProtocolRoot( + ) + } + //contexts +} 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 new file mode 100644 index 0000000000..7e053a37f8 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt @@ -0,0 +1,173 @@ +@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 + +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 [EngineProcessModel.kt:7] + */ +class RdSourceFindingStrategy private constructor( + private val _testsRelativePath: RdCall, + private val _getSourceRelativePath: RdCall, + private val _getSourceFile: RdCall +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + serializers.register(SourceStrategeMethodArgs) + } + + + @JvmStatic + @JvmName("internalCreateModel") + @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): RdSourceFindingStrategy { + @Suppress("DEPRECATION") + return create(lifetime, protocol) + } + + @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) + + return RdSourceFindingStrategy().apply { + identify(protocol.identity, RdId.Null.mix("RdSourceFindingStrategy")) + bind(lifetime, protocol, "RdSourceFindingStrategy") + } + } + + private val __StringNullableSerializer = FrameworkMarshallers.String.nullable() + + const val serializationHash = -8019839448677987345L + + } + override val serializersOwner: ISerializersOwner get() = RdSourceFindingStrategy + override val serializationHash: Long get() = RdSourceFindingStrategy.serializationHash + + //fields + val testsRelativePath: RdCall get() = _testsRelativePath + val getSourceRelativePath: RdCall get() = _getSourceRelativePath + val getSourceFile: RdCall get() = _getSourceFile + //methods + //initializer + init { + _testsRelativePath.async = true + _getSourceRelativePath.async = true + _getSourceFile.async = true + } + + init { + bindableChildren.add("testsRelativePath" to _testsRelativePath) + bindableChildren.add("getSourceRelativePath" to _getSourceRelativePath) + bindableChildren.add("getSourceFile" to _getSourceFile) + } + + //secondary constructor + private constructor( + ) : this( + RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.String), + RdCall(SourceStrategeMethodArgs, FrameworkMarshallers.String), + RdCall(SourceStrategeMethodArgs, __StringNullableSerializer) + ) + + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("RdSourceFindingStrategy (") + printer.indent { + print("testsRelativePath = "); _testsRelativePath.print(printer); println() + print("getSourceRelativePath = "); _getSourceRelativePath.print(printer); println() + print("getSourceFile = "); _getSourceFile.print(printer); println() + } + printer.print(")") + } + //deepClone + override fun deepClone(): RdSourceFindingStrategy { + return RdSourceFindingStrategy( + _testsRelativePath.deepClonePolymorphic(), + _getSourceRelativePath.deepClonePolymorphic(), + _getSourceFile.deepClonePolymorphic() + ) + } + //contexts +} +val IProtocol.rdSourceFindingStrategy get() = getOrCreateExtension(RdSourceFindingStrategy::class) { @Suppress("DEPRECATION") RdSourceFindingStrategy.create(lifetime, this) } + + + +/** + * #### Generated from [EngineProcessModel.kt:8] + */ +data class SourceStrategeMethodArgs ( + val classFqn: String, + val extension: String? +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SourceStrategeMethodArgs::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SourceStrategeMethodArgs { + val classFqn = buffer.readString() + val extension = buffer.readNullable { buffer.readString() } + return SourceStrategeMethodArgs(classFqn, extension) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SourceStrategeMethodArgs) { + buffer.writeString(value.classFqn) + buffer.writeNullable(value.extension) { buffer.writeString(it) } + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SourceStrategeMethodArgs + + if (classFqn != other.classFqn) return false + if (extension != other.extension) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + classFqn.hashCode() + __r = __r*31 + if (extension != null) extension.hashCode() else 0 + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SourceStrategeMethodArgs (") + printer.indent { + print("classFqn = "); classFqn.print(printer); println() + print("extension = "); extension.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt new file mode 100644 index 0000000000..76a3814baa --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsModel.Generated.kt @@ -0,0 +1,216 @@ +@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 + +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 [SettingsModel.kt:7] + */ +class SettingsModel private constructor( + private val _settingFor: RdCall +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + serializers.register(SettingForArgument) + serializers.register(SettingForResult) + } + + + @JvmStatic + @JvmName("internalCreateModel") + @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): SettingsModel { + @Suppress("DEPRECATION") + return create(lifetime, protocol) + } + + @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) + + return SettingsModel().apply { + identify(protocol.identity, RdId.Null.mix("SettingsModel")) + bind(lifetime, protocol, "SettingsModel") + } + } + + + const val serializationHash = 5155891414073322635L + + } + override val serializersOwner: ISerializersOwner get() = SettingsModel + override val serializationHash: Long get() = SettingsModel.serializationHash + + //fields + val settingFor: RdCall get() = _settingFor + //methods + //initializer + init { + _settingFor.async = true + } + + init { + bindableChildren.add("settingFor" to _settingFor) + } + + //secondary constructor + private constructor( + ) : this( + RdCall(SettingForArgument, SettingForResult) + ) + + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SettingsModel (") + printer.indent { + print("settingFor = "); _settingFor.print(printer); println() + } + printer.print(")") + } + //deepClone + override fun deepClone(): SettingsModel { + return SettingsModel( + _settingFor.deepClonePolymorphic() + ) + } + //contexts +} +val IProtocol.settingsModel get() = getOrCreateExtension(SettingsModel::class) { @Suppress("DEPRECATION") SettingsModel.create(lifetime, this) } + + + +/** + * #### Generated from [SettingsModel.kt:8] + */ +data class SettingForArgument ( + val key: String, + val propertyName: String +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SettingForArgument::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SettingForArgument { + val key = buffer.readString() + val propertyName = buffer.readString() + return SettingForArgument(key, propertyName) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SettingForArgument) { + buffer.writeString(value.key) + buffer.writeString(value.propertyName) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SettingForArgument + + if (key != other.key) return false + if (propertyName != other.propertyName) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + key.hashCode() + __r = __r*31 + propertyName.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SettingForArgument (") + printer.indent { + print("key = "); key.print(printer); println() + print("propertyName = "); propertyName.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [SettingsModel.kt:12] + */ +data class SettingForResult ( + val value: String? +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SettingForResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SettingForResult { + val value = buffer.readNullable { buffer.readString() } + return SettingForResult(value) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SettingForResult) { + buffer.writeNullable(value.value) { buffer.writeString(it) } + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SettingForResult + + if (value != other.value) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + if (value != null) value.hashCode() else 0 + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SettingForResult (") + printer.indent { + print("value = "); value.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.Generated.kt new file mode 100644 index 0000000000..62e91e16de --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/SettingsProtocolRoot.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.framework.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 [SettingsModel.kt:5] + */ +class SettingsProtocolRoot private constructor( +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + SettingsProtocolRoot.register(serializers) + SettingsModel.register(serializers) + } + + + + + + const val serializationHash = 6206621683627449183L + + } + override val serializersOwner: ISerializersOwner get() = SettingsProtocolRoot + override val serializationHash: Long get() = SettingsProtocolRoot.serializationHash + + //fields + //methods + //initializer + //secondary constructor + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SettingsProtocolRoot (") + printer.print(")") + } + //deepClone + override fun deepClone(): SettingsProtocolRoot { + return SettingsProtocolRoot( + ) + } + //contexts +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt new file mode 100644 index 0000000000..68adbb1295 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt @@ -0,0 +1,21 @@ +package org.utbot.sarif + +import kotlinx.coroutines.runBlocking +import org.utbot.framework.process.generated.RdSourceFindingStrategy +import org.utbot.framework.process.generated.SourceStrategeMethodArgs +import java.io.File + +class RdSourceFindingStrategyFacade(private val realStrategy: RdSourceFindingStrategy): SourceFindingStrategy() { + override val testsRelativePath: String + get() = runBlocking { realStrategy.testsRelativePath.startSuspending(Unit) } + + override fun getSourceRelativePath(classFqn: String, extension: String?): String = runBlocking { + realStrategy.getSourceRelativePath.startSuspending(SourceStrategeMethodArgs(classFqn, extension)) + } + + override fun getSourceFile(classFqn: String, extension: String?): File? = runBlocking { + realStrategy.getSourceFile.startSuspending(SourceStrategeMethodArgs(classFqn, extension))?.let { + File(it) + } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt index d2bd6d738c..2166156bac 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt @@ -340,6 +340,8 @@ class SarifReport( return if (utExecution is UtSymbolicExecution) { val lastPathLine = try { + // path/fullPath might be empty when engine executes in another process - + // soot entities cannot be passed to the main process because kryo cannot deserialize them utExecution.path.lastOrNull()?.stmt?.javaSourceStartLineNumber } catch (t: Throwable) { null 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 49564092a4..64a963fd35 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -30,7 +30,7 @@ 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.rd.UtRdKLoggerFactory +import org.utbot.rd.loggers.UtRdKLoggerFactory private val logger = KotlinLogging.logger {} @@ -125,7 +125,7 @@ class ConcreteExecutor> p var defaultPathsToDependencyClasses = "" init { - Logger.set(Lifetime.Eternal, UtRdKLoggerFactory) + Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) Runtime.getRuntime().addShutdownHook(thread(start = false) { defaultPool.close() }) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Settings.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Settings.kt index ce290b11b6..0b3cf8c032 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Settings.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Settings.kt @@ -30,5 +30,13 @@ object Settings { */ const val runChildProcessWithDebug = false + /** + * Property useful only for idea + * If true - runs engine process with the ability to attach a debugger + * @see runChildProcessWithDebug + * @see org.utbot.intellij.plugin.process.EngineProcess + */ + const val runIdeaProcessWithDebug = false + var defaultConcreteExecutorPoolSize = 10 } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt index 7971f09ccb..c80e1a7714 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/agent/DynamicClassTransformer.kt @@ -1,12 +1,16 @@ package org.utbot.instrumentation.agent +import com.jetbrains.rd.util.error +import com.jetbrains.rd.util.getLogger +import com.jetbrains.rd.util.info import org.utbot.common.asPathToFile import org.utbot.framework.plugin.api.util.UtContext -import org.utbot.instrumentation.process.logError -import org.utbot.instrumentation.process.logInfo import java.lang.instrument.ClassFileTransformer import java.security.ProtectionDomain + +private val logger = getLogger("DynamicClassTransformer") + /** * Transformer, which will transform only classes with certain names. */ @@ -32,13 +36,13 @@ class DynamicClassTransformer : ClassFileTransformer { return if (pathToClassfile in pathsToUserClasses || packsToAlwaysTransform.any(className::startsWith) ) { - logInfo { "Transforming: $className" } + logger.info { "Transforming: $className" } transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer) } else { null } } catch (e: Throwable) { - logError { "Error while transforming: ${e.stackTraceToString()}" } + logger.error { "Error while transforming: ${e.stackTraceToString()}" } throw e } finally { UtContext.currentContext()?.stopWatch?.start() diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt index 75d0cef781..6676be0da4 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt @@ -1,5 +1,7 @@ package org.utbot.instrumentation.instrumentation.et +import com.jetbrains.rd.util.error +import com.jetbrains.rd.util.getLogger import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.instrumentation.Settings @@ -9,7 +11,6 @@ import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import org.objectweb.asm.Type import org.objectweb.asm.commons.LocalVariablesSorter -import org.utbot.instrumentation.process.logError sealed class InstructionData { abstract val line: Int @@ -107,6 +108,7 @@ class ProcessingStorage { } } +private val logger = getLogger() /** * Storage to which instrumented classes will write execution data. @@ -157,7 +159,7 @@ object RuntimeTraceStorage { val loggedTip = alreadyLoggedIncreaseStackSizeTip if (!loggedTip) { alreadyLoggedIncreaseStackSizeTip = true - logError { "Stack overflow (increase stack size Settings.TRACE_ARRAY_SIZE)" } + logger.error { "Stack overflow (increase stack size Settings.TRACE_ARRAY_SIZE)" } } } } 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 31393d1717..4121858d16 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 @@ -1,45 +1,30 @@ package org.utbot.instrumentation.process -import com.jetbrains.rd.framework.* -import com.jetbrains.rd.framework.impl.RdCall -import com.jetbrains.rd.util.ILoggerFactory -import com.jetbrains.rd.util.LogLevel -import com.jetbrains.rd.util.Logger -import com.jetbrains.rd.util.defaultLogFormat +import com.jetbrains.rd.util.* import com.jetbrains.rd.util.lifetime.Lifetime -import com.jetbrains.rd.util.lifetime.LifetimeDefinition -import com.jetbrains.rd.util.lifetime.plusAssign -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeoutOrNull +import kotlinx.coroutines.* import org.utbot.common.* 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.childCreatedFileName +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.ProtocolModel -import org.utbot.instrumentation.rd.obtainClientIO -import org.utbot.instrumentation.rd.processSyncDirectory -import org.utbot.instrumentation.rd.signalChildReady +import org.utbot.instrumentation.rd.generated.childProcessModel import org.utbot.instrumentation.util.KryoHelper -import org.utbot.rd.UtRdCoroutineScope -import org.utbot.rd.adviseForConditionAsync +import org.utbot.rd.CallsSynchronizer +import org.utbot.rd.ClientProtocolBuilder +import org.utbot.rd.findRdPort +import org.utbot.rd.loggers.UtRdConsoleLoggerFactory import java.io.File import java.io.OutputStream import java.io.PrintStream import java.net.URLClassLoader import java.security.AllPermission -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.concurrent.TimeUnit import kotlin.system.measureTimeMillis -import org.utbot.framework.plugin.api.FieldId -import org.utbot.instrumentation.rd.generated.ComputeStaticFieldResult +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds /** * We use this ClassLoader to separate user's classes and our dependency classes. @@ -64,57 +49,25 @@ internal object HandlerClassesLoader : URLClassLoader(emptyArray()) { } } -private typealias ChildProcessLogLevel = LogLevel -private val logLevel = ChildProcessLogLevel.Info - -// Logging -private val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS") -private inline fun log(level: ChildProcessLogLevel, any: () -> Any?) { - if (level < logLevel) - return - - System.err.println(LocalDateTime.now().format(dateFormatter) + " ${level.name.uppercase()}| ${any()}") -} - -// errors that must be address -internal inline fun logError(any: () -> Any?) { - log(ChildProcessLogLevel.Error, any) -} - -// default log level for irregular useful messages that does not pollute log -internal inline fun logInfo(any: () -> Any?) { - log(ChildProcessLogLevel.Info, any) -} - -// log level for frequent messages useful for debugging -internal inline fun logDebug(any: () -> Any?) { - log(ChildProcessLogLevel.Debug, any) -} - -// log level for internal rd logs and frequent messages -// heavily pollutes log, useful only when debugging rpc -// probably contains no info about utbot -internal fun logTrace(any: () -> Any?) { - log(ChildProcessLogLevel.Trace, any) -} - -private enum class State { - STARTED, - ENDED -} - -private val messageFromMainTimeoutMillis: Long = TimeUnit.SECONDS.toMillis(120) -private val synchronizer: Channel = Channel(1) - /** * Command-line option to disable the sandbox */ const val DISABLE_SANDBOX_OPTION = "--disable-sandbox" +private val defaultLogLevel = LogLevel.Info +private val logger = getLogger("ChildProcess") +private val messageFromMainTimeout: Duration = 120.seconds /** * It should be compiled into separate jar file (child_process.jar) and be run with an agent (agent.jar) option. */ -suspend fun main(args: Array) = runBlocking { +fun main(args: Array) = runBlocking { + // We don't want user code to litter the standard output, so we redirect it. + val tmpStream = PrintStream(object : OutputStream() { + override fun write(b: Int) {} + }) + + System.setOut(tmpStream) + if (!args.contains(DISABLE_SANDBOX_OPTION)) { permissions { // Enable all permissions for instrumentation. @@ -122,91 +75,39 @@ suspend fun main(args: Array) = runBlocking { +AllPermission() } } - // 0 - auto port for server, should not be used here - val port = args.find { it.startsWith(serverPortProcessArgumentTag) } - ?.run { split("=").last().toInt().coerceIn(1..65535) } - ?: throw IllegalArgumentException("No port provided") - - val pid = currentProcessPid.toInt() - val def = LifetimeDefinition() - launch { - var lastState = State.STARTED - while (true) { - val current: State? = - withTimeoutOrNull(messageFromMainTimeoutMillis) { - synchronizer.receive() - } - if (current == null) { - if (lastState == State.ENDED) { - // process is waiting for command more than expected, better die - logInfo { "terminating lifetime" } - def.terminate() - break - } - } - else { - lastState = current - } - } - } + Logger.set(Lifetime.Eternal, UtRdConsoleLoggerFactory(defaultLogLevel, System.err)) + val port = findRdPort(args) - def.usingNested { lifetime -> - lifetime += { logInfo { "lifetime terminated" } } - try { - logInfo {"pid - $pid"} - logInfo {"isJvm8 - $isJvm8, isJvm9Plus - $isJvm9Plus, isWindows - $isWindows"} - initiate(lifetime, port, pid) - } finally { - val syncFile = File(processSyncDirectory, childCreatedFileName(pid)) - - if (syncFile.exists()) { - logInfo { "sync file existed" } - syncFile.delete() - } - } - } -} - -private fun measureExecutionForTermination(block: () -> T): T = runBlocking { try { - synchronizer.send(State.STARTED) - return@runBlocking block() - } - finally { - synchronizer.send(State.ENDED) + ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) { + val kryoHelper = KryoHelper(lifetime) + logger.info { "setup started" } + childProcessModel.setup(kryoHelper, it) + logger.info { "setup ended" } + } + } catch (e: Throwable) { + logger.error { "Terminating process because exception occurred: ${e.stackTraceToString()}" } } + logger.info { "runBlocking ending" } +}.also { + logger.info { "runBlocking ended" } } private lateinit var pathsToUserClasses: Set private lateinit var pathsToDependencyClasses: Set private lateinit var instrumentation: Instrumentation<*> -private fun RdCall.measureExecutionForTermination(block: (T) -> R) { - this.set { it -> - runBlocking { - measureExecutionForTermination { - try { - block(it) - } catch (e: Throwable) { - logError { e.stackTraceToString() } - throw e - } - } - } - } -} - -private fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Unit) { - warmup.measureExecutionForTermination { - logDebug { "received warmup request" } +private fun ChildProcessModel.setup(kryoHelper: KryoHelper, synchronizer: CallsSynchronizer) { + synchronizer.measureExecutionForTermination(warmup) { + logger.debug { "received warmup request" } val time = measureTimeMillis { HandlerClassesLoader.scanForClasses("").toList() // here we transform classes } - logDebug { "warmup finished in $time ms" } + logger.debug { "warmup finished in $time ms" } } - invokeMethodCommand.measureExecutionForTermination { params -> - logDebug { "received invokeMethod request: ${params.classname}, ${params.signature}" } + synchronizer.measureExecutionForTermination(invokeMethodCommand) { params -> + logger.debug { "received invokeMethod request: ${params.classname}, ${params.signature}" } val clazz = HandlerClassesLoader.loadClass(params.classname) val res = instrumentation.invoke( clazz, @@ -215,21 +116,19 @@ private fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Unit) { kryoHelper.readObject(params.parameters) ) - logDebug { "invokeMethod result: $res" } + logger.debug { "invokeMethod result: $res" } InvokeMethodCommandResult(kryoHelper.writeObject(res)) } - setInstrumentation.measureExecutionForTermination { params -> - logDebug { "setInstrumentation request" } + synchronizer.measureExecutionForTermination(setInstrumentation) { params -> + logger.debug { "setInstrumentation request" } instrumentation = kryoHelper.readObject(params.instrumentation) - logTrace { "instrumentation - ${instrumentation.javaClass.name} " } + logger.trace { "instrumentation - ${instrumentation.javaClass.name} " } Agent.dynamicClassTransformer.transformer = instrumentation // classTransformer is set Agent.dynamicClassTransformer.addUserPaths(pathsToUserClasses) instrumentation.init(pathsToUserClasses) } - addPaths.measureExecutionForTermination { params -> - logDebug { "addPaths request" } - logTrace { "path to userClasses - ${params.pathsToUserClasses}"} - logTrace { "path to dependencyClasses - ${params.pathsToDependencyClasses}"} + synchronizer.measureExecutionForTermination(addPaths) { params -> + logger.debug { "addPaths request" } pathsToUserClasses = params.pathsToUserClasses.split(File.pathSeparatorChar).toSet() pathsToDependencyClasses = params.pathsToDependencyClasses.split(File.pathSeparatorChar).toSet() HandlerClassesLoader.addUrls(pathsToUserClasses) @@ -237,84 +136,15 @@ private fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Unit) { kryoHelper.setKryoClassLoader(HandlerClassesLoader) // Now kryo will use our classloader when it encounters unregistered class. UtContext.setUtContext(UtContext(HandlerClassesLoader)) } - stopProcess.measureExecutionForTermination { - logDebug { "stop request" } - onStop() + synchronizer.measureExecutionForTermination(stopProcess) { + logger.debug { "stop request" } + synchronizer.stopProtocol() } - collectCoverage.measureExecutionForTermination { params -> - logDebug { "collect coverage request" } + synchronizer.measureExecutionForTermination(collectCoverage) { params -> + logger.debug { "collect coverage request" } val anyClass: Class<*> = kryoHelper.readObject(params.clazz) - logTrace { "class - ${anyClass.name}" } + logger.debug { "class - ${anyClass.name}" } val result = (instrumentation as CoverageInstrumentation).collectCoverageInfo(anyClass) CollectCoverageResult(kryoHelper.writeObject(result)) } - computeStaticField.measureExecutionForTermination { params -> - val fieldId = kryoHelper.readObject(params.fieldId) - val result = instrumentation.getStaticField(fieldId) - ComputeStaticFieldResult(kryoHelper.writeObject(result)) - } -} - -private suspend fun initiate(lifetime: Lifetime, port: Int, pid: Int) { - // We don't want user code to litter the standard output, so we redirect it. - val tmpStream = PrintStream(object : OutputStream() { - override fun write(b: Int) {} - }) - System.setOut(tmpStream) - - Logger.set(lifetime, object : ILoggerFactory { - override fun getLogger(category: String) = object : Logger { - override fun isEnabled(level: LogLevel): Boolean { - return level >= logLevel - } - - override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { - val msg = defaultLogFormat(category, level, message, throwable) - - log(logLevel) { msg } - } - - } - }) - - val deferred = CompletableDeferred() - lifetime.onTermination { deferred.complete(Unit) } - val kryoHelper = KryoHelper(lifetime) - logInfo { "kryo created" } - - val clientProtocol = Protocol( - "ChildProcess", - Serializers(), - Identities(IdKind.Client), - UtRdCoroutineScope.scheduler, - SocketWire.Client(lifetime, UtRdCoroutineScope.scheduler, port), - lifetime - ) - val (sync, protocolModel) = obtainClientIO(lifetime, clientProtocol) - - protocolModel.setup(kryoHelper) { - deferred.complete(Unit) - } - signalChildReady(pid) - logInfo { "IO obtained" } - - val answerFromMainProcess = sync.adviseForConditionAsync(lifetime) { - if (it == "main") { - logTrace { "received from main" } - measureExecutionForTermination { - sync.fire("child") - } - true - } else { - false - } - } - - try { - answerFromMainProcess.await() - logInfo { "starting instrumenting" } - deferred.await() - } catch (e: Throwable) { - logError { "Terminating process because exception occurred: ${e.stackTraceToString()}" } - } } \ No newline at end of file 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 df00534cd9..bcb9dd2868 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 @@ -9,11 +9,11 @@ import org.utbot.framework.UtSettings import org.utbot.framework.plugin.services.WorkingDirService import org.utbot.instrumentation.Settings import org.utbot.instrumentation.agent.DynamicClassTransformer +import org.utbot.rd.rdPortArgument import java.io.File import kotlin.random.Random private val logger = KotlinLogging.logger {} -const val serverPortProcessArgumentTag = "serverPort" class ChildProcessRunner { private val id = Random.nextLong() @@ -38,7 +38,7 @@ class ChildProcessRunner { var errorLogFile: File = NULL_FILE fun start(port: Int): Process { - val portArgument = "$serverPortProcessArgumentTag=$port" + val portArgument = rdPortArgument(port) logger.debug { "Starting child process: ${cmds.joinToString(" ")} $portArgument" } processSeqN++ @@ -114,7 +114,7 @@ class ChildProcessRunner { } ?: run { logger.debug("Failed to find jar in the resources. Trying to find it in the classpath.") ChildProcessRunner::class.java.classLoader - .scanForResourcesContaining(DynamicClassTransformer::class.java.packageName) + .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/InstrumentationIO.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentationIO.kt deleted file mode 100644 index b9ba0ddd26..0000000000 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentationIO.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.utbot.instrumentation.rd - -import com.jetbrains.rd.framework.Protocol -import com.jetbrains.rd.framework.base.static -import com.jetbrains.rd.framework.impl.RdSignal -import com.jetbrains.rd.util.lifetime.Lifetime -import org.utbot.common.utBotTempDirectory -import org.utbot.instrumentation.rd.generated.ProtocolModel -import org.utbot.instrumentation.rd.generated.protocolModel -import org.utbot.rd.pump -import java.io.File - -const val rdProcessDirName = "rdProcessSync" -val processSyncDirectory = File(utBotTempDirectory.toFile(), rdProcessDirName) - -internal suspend fun obtainClientIO(lifetime: Lifetime, protocol: Protocol): Pair, ProtocolModel> { - return protocol.scheduler.pump(lifetime) { - val sync = RdSignal().static(1).apply { - async = true - bind(lifetime, protocol, rdid.toString()) - } - sync to protocol.protocolModel - } -} - -internal fun childCreatedFileName(pid: Int): String { - return "$pid.created" -} - -internal fun signalChildReady(pid: Int) { - processSyncDirectory.mkdirs() - - val signalFile = File(processSyncDirectory, childCreatedFileName(pid)) - - if (signalFile.exists()) { - signalFile.delete() - } - - val created = signalFile.createNewFile() - - if (!created) { - throw IllegalStateException("cannot create signal file") - } -} \ No newline at end of file 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 47ca22e050..ba7d09d62e 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 @@ -1,26 +1,19 @@ package org.utbot.instrumentation.rd -import com.jetbrains.rd.framework.base.static -import com.jetbrains.rd.framework.impl.RdSignal import com.jetbrains.rd.util.lifetime.Lifetime -import com.jetbrains.rd.util.lifetime.isAlive -import kotlinx.coroutines.delay import mu.KotlinLogging -import org.utbot.common.getPid 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.ProtocolModel +import org.utbot.instrumentation.rd.generated.ChildProcessModel import org.utbot.instrumentation.rd.generated.SetInstrumentationParams -import org.utbot.instrumentation.rd.generated.protocolModel +import org.utbot.instrumentation.rd.generated.childProcessModel import org.utbot.instrumentation.util.KryoHelper -import org.utbot.rd.* -import java.io.File -import java.nio.file.Files -import java.util.concurrent.atomic.AtomicBoolean +import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.startUtProcessWithRdServer +import org.utbot.rd.terminateOnException private val logger = KotlinLogging.logger {} -private const val fileWaitTimeoutMillis = 10L /** * Main goals of this class: @@ -31,57 +24,11 @@ class UtInstrumentationProcess private constructor( private val classLoader: ClassLoader?, private val rdProcess: ProcessWithRdServer ) : ProcessWithRdServer by rdProcess { - private val sync = RdSignal().static(1).apply { async = true } val kryoHelper = KryoHelper(lifetime.createNested()).apply { classLoader?.let { setKryoClassLoader(it) } } - val protocolModel: ProtocolModel - get() = protocol.protocolModel - - private suspend fun init(): UtInstrumentationProcess { - protocol.scheduler.pump(lifetime) { - sync.bind(lifetime, protocol, sync.rdid.toString()) - protocol.protocolModel - } - processSyncDirectory.mkdirs() - - // there 2 stages at rd protocol initialization: - // 1. we need to bind all entities - for ex. generated model and custom signal - // because we cannot operate with unbound - // 2. we need to wait when all that entities bound on the other side - // because when we fire something that is not bound on another side - we will lose this call - // to guarantee 2nd stage success - there is custom simple synchronization: - // 1. child process will create file "${processId}.created" - this indicates that child process is ready to receive messages - // 2. we will test the connection via sync RdSignal - // only then we can successfully start operating - val pid = process.getPid.toInt() - val syncFile = File(processSyncDirectory, childCreatedFileName(pid)) - - while (lifetime.isAlive) { - if (Files.deleteIfExists(syncFile.toPath())) { - logger.trace { "process $pid: signal file deleted connecting" } - break - } - - delay(fileWaitTimeoutMillis) - } - - val messageFromChild = sync.adviseForConditionAsync(lifetime) { it == "child" } - - while(messageFromChild.isActive) { - sync.fire("main") - delay(10) - } - - lifetime.onTermination { - if (syncFile.exists()) { - logger.trace { "process $pid: on terminating syncFile existed" } - syncFile.delete() - } - } - - return this - } + val protocolModel: ChildProcessModel + get() = protocol.childProcessModel companion object { private suspend fun > invokeImpl( @@ -96,12 +43,14 @@ class UtInstrumentationProcess private constructor( lifetime = lifetime ) { childProcessRunner.start(it) - } + }.initModels { childProcessModel }.awaitSignal() + logger.trace("rd process started") + val proc = UtInstrumentationProcess( classLoader, rdProcess - ).init() + ) proc.lifetime.onTermination { logger.trace { "process is terminating" } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt similarity index 93% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt index fa70bab72b..ae552c67a5 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessModel.Generated.kt @@ -15,9 +15,9 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [ProtocolRoot.kt:7] + * #### Generated from [ChildProcessModel.kt:7] */ -class ProtocolModel private constructor( +class ChildProcessModel private constructor( private val _addPaths: RdCall, private val _warmup: RdCall, private val _setInstrumentation: RdCall, @@ -45,28 +45,28 @@ class ProtocolModel private constructor( @JvmStatic @JvmName("internalCreateModel") @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) - internal fun createModel(lifetime: Lifetime, protocol: IProtocol): ProtocolModel { + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): ChildProcessModel { @Suppress("DEPRECATION") return create(lifetime, protocol) } @JvmStatic - @Deprecated("Use protocol.protocolModel or revise the extension scope instead", ReplaceWith("protocol.protocolModel")) - fun create(lifetime: Lifetime, protocol: IProtocol): ProtocolModel { - ProtocolRoot.register(protocol.serializers) + @Deprecated("Use protocol.childProcessModel or revise the extension scope instead", ReplaceWith("protocol.childProcessModel")) + fun create(lifetime: Lifetime, protocol: IProtocol): ChildProcessModel { + ChildProcessProtocolRoot.register(protocol.serializers) - return ProtocolModel().apply { - identify(protocol.identity, RdId.Null.mix("ProtocolModel")) - bind(lifetime, protocol, "ProtocolModel") + return ChildProcessModel().apply { + identify(protocol.identity, RdId.Null.mix("ChildProcessModel")) + bind(lifetime, protocol, "ChildProcessModel") } } - const val serializationHash = -3299689793276292923L + const val serializationHash = 3283744426733090208L } - override val serializersOwner: ISerializersOwner get() = ProtocolModel - override val serializationHash: Long get() = ProtocolModel.serializationHash + override val serializersOwner: ISerializersOwner get() = ChildProcessModel + override val serializationHash: Long get() = ChildProcessModel.serializationHash //fields @@ -146,7 +146,7 @@ class ProtocolModel private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("ProtocolModel (") + printer.println("ChildProcessModel (") printer.indent { print("addPaths = "); _addPaths.print(printer); println() print("warmup = "); _warmup.print(printer); println() @@ -159,8 +159,8 @@ class ProtocolModel private constructor( printer.print(")") } //deepClone - override fun deepClone(): ProtocolModel { - return ProtocolModel( + override fun deepClone(): ChildProcessModel { + return ChildProcessModel( _addPaths.deepClonePolymorphic(), _warmup.deepClonePolymorphic(), _setInstrumentation.deepClonePolymorphic(), @@ -172,12 +172,12 @@ class ProtocolModel private constructor( } //contexts } -val IProtocol.protocolModel get() = getOrCreateExtension(ProtocolModel::class) { @Suppress("DEPRECATION") ProtocolModel.create(lifetime, this) } +val IProtocol.childProcessModel get() = getOrCreateExtension(ChildProcessModel::class) { @Suppress("DEPRECATION") ChildProcessModel.create(lifetime, this) } /** - * #### Generated from [ProtocolRoot.kt:8] + * #### Generated from [ChildProcessModel.kt:8] */ data class AddPathsParams ( val pathsToUserClasses: String, @@ -240,7 +240,7 @@ data class AddPathsParams ( /** - * #### Generated from [ProtocolRoot.kt:28] + * #### Generated from [ChildProcessModel.kt:28] */ data class CollectCoverageParams ( val clazz: ByteArray @@ -297,7 +297,7 @@ data class CollectCoverageParams ( /** - * #### Generated from [ProtocolRoot.kt:32] + * #### Generated from [ChildProcessModel.kt:32] */ data class CollectCoverageResult ( val coverageInfo: ByteArray @@ -354,7 +354,7 @@ data class CollectCoverageResult ( /** - * #### Generated from [ProtocolRoot.kt:36] + * #### Generated from [ChildProcessModel.kt:36] */ data class ComputeStaticFieldParams ( val fieldId: ByteArray @@ -411,7 +411,7 @@ data class ComputeStaticFieldParams ( /** - * #### Generated from [ProtocolRoot.kt:40] + * #### Generated from [ChildProcessModel.kt:40] */ data class ComputeStaticFieldResult ( val result: ByteArray @@ -468,7 +468,7 @@ data class ComputeStaticFieldResult ( /** - * #### Generated from [ProtocolRoot.kt:17] + * #### Generated from [ChildProcessModel.kt:17] */ data class InvokeMethodCommandParams ( val classname: String, @@ -543,7 +543,7 @@ data class InvokeMethodCommandParams ( /** - * #### Generated from [ProtocolRoot.kt:24] + * #### Generated from [ChildProcessModel.kt:24] */ data class InvokeMethodCommandResult ( val result: ByteArray @@ -600,7 +600,7 @@ data class InvokeMethodCommandResult ( /** - * #### Generated from [ProtocolRoot.kt:13] + * #### Generated from [ChildProcessModel.kt:13] */ data class SetInstrumentationParams ( val instrumentation: ByteArray diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolRoot.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt similarity index 65% rename from utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolRoot.Generated.kt rename to utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt index 7551ac8c91..7969676ff7 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolRoot.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ChildProcessProtocolRoot.Generated.kt @@ -15,28 +15,28 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [ProtocolRoot.kt:5] + * #### Generated from [ChildProcessModel.kt:5] */ -class ProtocolRoot private constructor( +class ChildProcessProtocolRoot private constructor( ) : RdExtBase() { //companion companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { - ProtocolRoot.register(serializers) - ProtocolModel.register(serializers) + ChildProcessProtocolRoot.register(serializers) + ChildProcessModel.register(serializers) } - const val serializationHash = -479905474426893924L + const val serializationHash = -2158664525887799313L } - override val serializersOwner: ISerializersOwner get() = ProtocolRoot - override val serializationHash: Long get() = ProtocolRoot.serializationHash + override val serializersOwner: ISerializersOwner get() = ChildProcessProtocolRoot + override val serializationHash: Long get() = ChildProcessProtocolRoot.serializationHash //fields //methods @@ -46,12 +46,12 @@ class ProtocolRoot private constructor( //hash code trait //pretty print override fun print(printer: PrettyPrinter) { - printer.println("ProtocolRoot (") + printer.println("ChildProcessProtocolRoot (") printer.print(")") } //deepClone - override fun deepClone(): ProtocolRoot { - return ProtocolRoot( + override fun deepClone(): ChildProcessProtocolRoot { + return ChildProcessProtocolRoot( ) } //contexts diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt index f9414ad3c2..1b6f904d3c 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt @@ -6,6 +6,7 @@ import com.esotericsoftware.kryo.kryo5.SerializerFactory import com.esotericsoftware.kryo.kryo5.io.Input import com.esotericsoftware.kryo.kryo5.io.Output import com.esotericsoftware.kryo.kryo5.objenesis.instantiator.ObjectInstantiator +import com.esotericsoftware.kryo.kryo5.objenesis.strategy.InstantiatorStrategy import com.esotericsoftware.kryo.kryo5.objenesis.strategy.StdInstantiatorStrategy import com.esotericsoftware.kryo.kryo5.serializers.JavaSerializer import com.esotericsoftware.kryo.kryo5.util.DefaultInstantiatorStrategy @@ -17,7 +18,7 @@ import java.io.ByteArrayOutputStream /** * Helpful class for working with the kryo. */ -class KryoHelper internal constructor( +class KryoHelper constructor( private val lifetime: Lifetime ) { private val outputBuffer = ByteArrayOutputStream() @@ -33,6 +34,28 @@ class KryoHelper internal constructor( } } + fun register(clazz: Class, serializer: Serializer) { + sendKryo.register(clazz, serializer) + receiveKryo.register(clazz, serializer) + } + + private fun addInstantiatorOnKryo(kryo: Kryo, clazz: Class, factory: () -> T) { + val instantiator = kryo.instantiatorStrategy + kryo.instantiatorStrategy = object : InstantiatorStrategy { + override fun newInstantiatorOf(type: Class): ObjectInstantiator { + return if (type === clazz) { + ObjectInstantiator { factory() as R } + } + else + instantiator.newInstantiatorOf(type) + } + } + } + fun addInstantiator(clazz: Class, factory: () -> T) { + addInstantiatorOnKryo(sendKryo, clazz, factory) + addInstantiatorOnKryo(receiveKryo, clazz, factory) + } + fun setKryoClassLoader(classLoader: ClassLoader) { sendKryo.classLoader = classLoader receiveKryo.classLoader = classLoader diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 4377a5c443..d27a08588e 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -5,6 +5,8 @@ val jacksonVersion: String? by rootProject val ideType: String? by rootProject val pythonCommunityPluginVersion: String? by rootProject val pythonUltimatePluginVersion: String? by rootProject +val sootCommitHash: String? by rootProject +val kryoVersion: String? by rootProject plugins { id("org.jetbrains.intellij") version "1.7.0" @@ -16,7 +18,7 @@ intellij { val jvmPlugins = listOf( "java", - "org.jetbrains.kotlin:212-1.7.10-release-333-IJ5457.46" + "org.jetbrains.kotlin:222-1.7.20-release-201-IJ4167.29" ) val pythonCommunityPlugins = listOf( @@ -41,7 +43,7 @@ intellij { } ) - version.set("212.5712.43") + version.set("222.4167.29") type.set(ideType) } @@ -65,11 +67,15 @@ tasks { patchPluginXml { sinceBuild.set("212") - untilBuild.set("221.*") + untilBuild.set("222.*") } } dependencies { +// implementation("com.github.UnitTestBot:soot:${sootCommitHash}") + 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.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) implementation("org.apache.httpcomponents.client5:httpclient5:5.1") 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 aa10ce6fec..6e4d0f6e7e 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 @@ -20,26 +20,14 @@ import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.util.Computable import com.intellij.openapi.wm.ToolWindowManager -import com.intellij.psi.JavaDirectoryService -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiClassOwner -import com.intellij.psi.PsiComment -import com.intellij.psi.PsiDirectory -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiFileFactory -import com.intellij.psi.PsiManager -import com.intellij.psi.PsiMethod -import com.intellij.psi.SmartPointerManager -import com.intellij.psi.SmartPsiElementPointer +import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.search.GlobalSearchScopesCore -import com.intellij.refactoring.util.classMembers.MemberInfo import com.intellij.testIntegration.TestIntegrationUtils import com.intellij.util.IncorrectOperationException import com.siyeh.ig.psiutils.ImportUtils +import mu.KotlinLogging import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.core.ShortenReferences @@ -53,50 +41,34 @@ import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.scripting.resolve.classId import org.utbot.common.HTML_LINE_SEPARATOR import org.utbot.common.PathUtil.toHtmlLinkTag -import org.utbot.common.allNestedClasses -import org.utbot.common.appendHtmlLine import org.utbot.framework.codegen.Import import org.utbot.framework.codegen.ParametrizedTestSource import org.utbot.framework.codegen.RegularImport import org.utbot.framework.codegen.StaticImport -import org.utbot.framework.codegen.model.CodeGenerator -import org.utbot.framework.codegen.model.CodeGeneratorResult import org.utbot.framework.codegen.model.UtilClassKind import org.utbot.framework.codegen.model.UtilClassKind.Companion.UT_UTILS_CLASS_NAME -import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport +import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.UtMethodTestSet -import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.util.Conflict import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.models.packageName +import org.utbot.intellij.plugin.process.EngineProcess +import org.utbot.intellij.plugin.process.RdGTestenerationResult import org.utbot.intellij.plugin.sarif.SarifReportIdea import org.utbot.intellij.plugin.sarif.SourceFindingStrategyIdea -import org.utbot.intellij.plugin.ui.DetailsTestsReportNotifier -import org.utbot.intellij.plugin.ui.SarifReportNotifier -import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener -import org.utbot.intellij.plugin.ui.TestsReportNotifier -import org.utbot.intellij.plugin.ui.WarningTestsReportNotifier +import org.utbot.intellij.plugin.ui.* import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater import org.utbot.intellij.plugin.ui.utils.suitableTestSourceRoots +import org.utbot.intellij.plugin.util.IntelliJApiHelper.Target.* +import org.utbot.intellij.plugin.util.IntelliJApiHelper.run import org.utbot.intellij.plugin.util.RunConfigurationHelper import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested -import org.utbot.intellij.plugin.util.signature import org.utbot.sarif.SarifReport import java.nio.file.Path import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import kotlin.reflect.KClass -import kotlin.reflect.full.functions -import mu.KotlinLogging -import org.utbot.intellij.plugin.util.IntelliJApiHelper.Target.* -import org.utbot.intellij.plugin.util.IntelliJApiHelper.run object CodeGenerationController { private val logger = KotlinLogging.logger {} @@ -104,53 +76,41 @@ object CodeGenerationController { private class UtilClassListener { var requiredUtilClassKind: UtilClassKind? = null - fun onTestClassGenerated(result: CodeGeneratorResult) { - requiredUtilClassKind = maxOfNullable(requiredUtilClassKind, result.utilClassKind) + fun onTestClassGenerated(result: UtilClassKind?) { + requiredUtilClassKind = maxOfNullable(requiredUtilClassKind, result) } } fun generateTests( model: GenerateTestsModel, - testSetsByClass: Map>, - psi2KClass: Map> + classesWithTests: Map, + psi2KClass: Map, + proc: EngineProcess ) { val baseTestDirectory = model.testSourceRoot?.toPsiDirectory(model.project) ?: return val allTestPackages = getPackageDirectories(baseTestDirectory) - val latch = CountDownLatch(testSetsByClass.size) - - val reports = mutableListOf() + val latch = CountDownLatch(classesWithTests.size) val testFilesPointers = mutableListOf>() val utilClassListener = UtilClassListener() - for (srcClass in testSetsByClass.keys) { - val testSets = testSetsByClass[srcClass] ?: continue + for ((srcClass, generateResult) in classesWithTests) { + val (count, testSetsId) = generateResult + if (count <= 0) continue try { val classPackageName = model.getTestClassPackageNameFor(srcClass) val testDirectory = allTestPackages[classPackageName] ?: baseTestDirectory val testClass = createTestClass(srcClass, testDirectory, model) ?: continue val testFilePointer = SmartPointerManager.getInstance(model.project).createSmartPsiElementPointer(testClass.containingFile) val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}") - run(EDT_LATER) { - runWriteCommandAction(model.project, "Generate tests with UtBot", null, { - try { - generateCodeAndReport( - srcClass, - cut, - testClass, - testFilePointer, - testSets, - model, - latch, - reports, - utilClassListener - ) - testFilesPointers.add(testFilePointer) - } catch (e: IncorrectOperationException) { - logger.error { e } - showCreatingClassError(model.project, createTestClassName(srcClass)) - } - }) - } + runWriteCommandAction(model.project, "Generate tests with UtBot", null, { + try { + generateCodeAndReport(proc, testSetsId, srcClass, cut, testClass, testFilePointer, model, latch, utilClassListener) + testFilesPointers.add(testFilePointer) + } catch (e: IncorrectOperationException) { + logger.error { e } + showCreatingClassError(model.project, createTestClassName(srcClass)) + } + }) } catch (e: IncorrectOperationException) { logger.error { e } showCreatingClassError(model.project, createTestClassName(srcClass)) @@ -163,19 +123,7 @@ object CodeGenerationController { run(WRITE_ACTION) { createUtilityClassIfNeed(utilClassListener, model, baseTestDirectory) run(EDT_LATER) { - try { - // Parametrized tests are not supported in tests report yet - // TODO JIRA:1507 - if (model.parametrizedTestSource != ParametrizedTestSource.PARAMETRIZE) { - showTestsReport(reports, model) - } - } catch (e: Exception) { - showErrorDialogLater( - model.project, - message = "Cannot save tests generation report: error occurred '${e.message}'", - title = "Failed to save tests report" - ) - } + proceedTestReport(proc, model) run(THREAD_POOL) { val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) @@ -183,6 +131,7 @@ object CodeGenerationController { if (model.runGeneratedTestsWithCoverage) { RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers) } + proc.forceTermination() } } } @@ -191,6 +140,21 @@ object CodeGenerationController { } } + private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) { + try { + // Parametrized tests are not supported in tests report yet + // TODO JIRA:1507 + if (model.parametrizedTestSource != ParametrizedTestSource.PARAMETRIZE) { + showTestsReport(proc, model) + } + } catch (e: Exception) { + showErrorDialogLater( + model.project, + message = "Cannot save tests generation report: error occurred '${e.message}'", + title = "Failed to save tests report" + ) + } + } private fun createUtilityClassIfNeed( utilClassListener: UtilClassListener, model: GenerateTestsModel, @@ -587,44 +551,42 @@ object CodeGenerationController { } private fun generateCodeAndReport( + proc: EngineProcess, + testSetsId: Long, srcClass: PsiClass, - classUnderTest: KClass<*>, + classUnderTest: ClassId, testClass: PsiClass, filePointer: SmartPsiElementPointer, - testSets: List, model: GenerateTestsModel, reportsCountDown: CountDownLatch, - reports: MutableList, utilClassListener: UtilClassListener ) { val classMethods = srcClass.extractClassMethodsIncludingNested(false) val paramNames = DumbService.getInstance(model.project) - .runReadActionInSmartMode(Computable { findMethodParamNames(classUnderTest, classMethods) }) - - val codeGenerator = CodeGenerator( - classUnderTest = classUnderTest.id, - generateUtilClassFile = true, - paramNames = paramNames.toMutableMap(), - testFramework = model.testFramework, - mockFramework = model.mockFramework, - codegenLanguage = model.codegenLanguage, - parameterizedTestSource = model.parametrizedTestSource, - staticsMocking = model.staticsMocking, - forceStaticMocking = model.forceStaticMocking, - generateWarningsForStaticMocking = model.generateWarningsForStaticMocking, - runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour, - hangingTestsTimeout = model.hangingTestsTimeout, - enableTestsTimeout = true, - testClassPackageName = testClass.packageName - ) - + .runReadActionInSmartMode(Computable { proc.findMethodParamNames(classUnderTest, classMethods) }) + 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) { - val codeGenerationResult = codeGenerator.generateAsStringWithTestReport(testSets) - utilClassListener.onTestClassGenerated(codeGenerationResult) - val generatedTestsCode = codeGenerationResult.generatedCode + val (generatedTestsCode, utilClassKind) = proc.render( + testSetsId, + classUnderTest, + paramNames.toMutableMap(), + generateUtilClassFile = true, + model.testFramework, + model.mockFramework, + model.staticsMocking, + model.forceStaticMocking, + model.generateWarningsForStaticMocking, + model.codegenLanguage, + model.parametrizedTestSource, + model.runtimeExceptionTestsBehaviour, + model.hangingTestsTimeout, + enableTestsTimeout = true, + testPackageName + ) + utilClassListener.onTestClassGenerated(utilClassKind) run(EDT_LATER) { run(WRITE_ACTION) { unblockDocument(testClass.project, editor.document) @@ -650,20 +612,15 @@ object CodeGenerationController { // uploading formatted code val file = filePointer.containingFile - val codeGenerationResultFormatted = - codeGenerationResult.copy(generatedCode = file?.text?: generatedTestsCode) - - // creating and saving reports - reports += codeGenerationResultFormatted.testsGenerationReport - - run(WRITE_ACTION) { - saveSarifReport( - testClassUpdated, - testSets, - model, - codeGenerationResultFormatted, - ) - } + + saveSarifReport( + proc, + testSetsId, + testClassUpdated, + classUnderTest, + model, + file?.text?: generatedTestsCode + ) unblockDocument(testClassUpdated.project, editor.document) reportsCountDown.countDown() @@ -693,30 +650,21 @@ object CodeGenerationController { } } - private fun findMethodParamNames(clazz: KClass<*>, methods: List): Map> { - val bySignature = methods.associate { it.signature() to it.paramNames() } - return clazz.allNestedClasses.flatMap { it.functions } - .mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } } - .toMap() - } - - private fun MemberInfo.paramNames(): List = - (this.member as PsiMethod).parameterList.parameters.map { it.name } - private fun saveSarifReport( + proc: EngineProcess, + testSetsId: Long, testClass: PsiClass, - testSets: List, + testClassId: ClassId, model: GenerateTestsModel, - testsCodeWithTestReport: CodeGeneratorResult, + generatedTestsCode: String, ) { val project = model.project - val generatedTestsCode = testsCodeWithTestReport.generatedCode try { // saving sarif report val sourceFinding = SourceFindingStrategyIdea(testClass) executeCommand(testClass.project, "Saving Sarif report") { - SarifReportIdea.createAndSave(model, testSets, generatedTestsCode, sourceFinding) + SarifReportIdea.createAndSave(proc, testSetsId, testClassId, model, generatedTestsCode, sourceFinding) } } catch (e: Exception) { logger.error { e } @@ -728,87 +676,17 @@ object CodeGenerationController { } } - private fun isEventLogAvailable(project: Project) = - ToolWindowManager.getInstance(project).getToolWindow("Event Log") != null - private fun eventLogMessage(): String = - """ + private fun eventLogMessage(project: Project): String? { + if (ToolWindowManager.getInstance(project).getToolWindow("Event Log") != null) + return """ See details in Event Log. """.trimIndent() - - private fun destinationWarningMessage(testPackageName: String?, classUnderTestPackageName: String): String? { - return if (classUnderTestPackageName != testPackageName) { - """ - Warning: Destination package $testPackageName does not match package of the class $classUnderTestPackageName. - This may cause unnecessary usage of reflection for protected or package-private fields and methods access. - """.trimIndent() - } else { - null - } + return null } - private fun showTestsReport(reports: List, model: GenerateTestsModel) { - var hasWarnings = false - require(reports.isNotEmpty()) - - val (notifyMessage, statistics) = if (reports.size == 1) { - val report = reports.first() - processInitialWarnings(report, model) - - val message = buildString { - appendHtmlLine(report.toString(isShort = true)) - - val classUnderTestPackageName = - report.classUnderTest.classId.packageFqName.toString() - - destinationWarningMessage(model.testPackageName, classUnderTestPackageName) - ?.let { - hasWarnings = true - appendHtmlLine(it) - appendHtmlLine() - } - if (isEventLogAvailable(model.project)) { - appendHtmlLine(eventLogMessage()) - } - } - hasWarnings = hasWarnings || report.hasWarnings - Pair(message, report.detailedStatistics) - } else { - val accumulatedReport = reports.first() - processInitialWarnings(accumulatedReport, model) - - val message = buildString { - appendHtmlLine("${reports.sumBy { it.executables.size }} tests generated for ${reports.size} classes.") - - if (accumulatedReport.initialWarnings.isNotEmpty()) { - accumulatedReport.initialWarnings.forEach { appendHtmlLine(it()) } - appendHtmlLine() - } - - // TODO maybe add statistics info here - - for (report in reports) { - val classUnderTestPackageName = - report.classUnderTest.classId.packageFqName.toString() - - hasWarnings = hasWarnings || report.hasWarnings - if (!model.isMultiPackage) { - val destinationWarning = - destinationWarningMessage(model.testPackageName, classUnderTestPackageName) - if (destinationWarning != null) { - hasWarnings = true - appendHtmlLine(destinationWarning) - appendHtmlLine() - } - } - } - if (isEventLogAvailable(model.project)) { - appendHtmlLine(eventLogMessage()) - } - } - - Pair(message, null) - } + private fun showTestsReport(proc: EngineProcess, model: GenerateTestsModel) { + val (notifyMessage, statistics, hasWarnings) = proc.generateTestsReport(model, eventLogMessage(model.project)) if (hasWarnings) { WarningTestsReportNotifier.notify(notifyMessage) @@ -819,44 +697,6 @@ object CodeGenerationController { statistics?.let { DetailsTestsReportNotifier.notify(it) } } - private fun processInitialWarnings(report: TestsGenerationReport, model: GenerateTestsModel) { - val hasInitialWarnings = model.conflictTriggers.triggered - - if (!hasInitialWarnings) { - return - } - - report.apply { - if (model.conflictTriggers[Conflict.ForceMockHappened] == true) { - initialWarnings.add { - """ - 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() - } - } - if (model.conflictTriggers[Conflict.ForceStaticMockHappened] == true) { - initialWarnings.add { - """ - 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() - } - } - if (model.conflictTriggers[Conflict.TestFrameworkConflict] == true) { - initialWarnings.add { - """ - 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() - } - } - } - } - @Suppress("unused") // this method was used in the past, not used in the present but may be used in the future private fun insertImports(testClass: PsiClass, imports: List, editor: Editor) { 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 9aad3aa474..6b1d0eb609 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 @@ -1,5 +1,6 @@ package org.utbot.intellij.plugin.generator +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.PathManager import com.intellij.openapi.application.ReadAction import com.intellij.openapi.application.invokeLater @@ -21,48 +22,35 @@ import com.intellij.task.ProjectTaskManager 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 mu.KotlinLogging import org.jetbrains.kotlin.idea.util.module -import org.utbot.analytics.EngineAnalyticsContext -import org.utbot.analytics.Predictors -import org.utbot.common.allNestedClasses -import org.utbot.engine.util.mockListeners.ForceMockListener -import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.TestCaseGenerator -import org.utbot.framework.plugin.api.UtMethodTestSet -import org.utbot.framework.plugin.api.testFlow -import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.JavaDocCommentStyle import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired -import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.framework.plugin.services.JdkInfoService +import org.utbot.framework.plugin.services.WorkingDirService import org.utbot.intellij.plugin.generator.CodeGenerationController.generateTests import org.utbot.intellij.plugin.models.GenerateTestsModel +import org.utbot.intellij.plugin.models.packageName +import org.utbot.intellij.plugin.process.EngineProcess +import org.utbot.intellij.plugin.process.RdGTestenerationResult +import org.utbot.intellij.plugin.settings.Settings import org.utbot.intellij.plugin.ui.GenerateTestsDialogWindow +import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater import org.utbot.intellij.plugin.ui.utils.suitableTestSourceRoots import org.utbot.intellij.plugin.ui.utils.testModules -import org.utbot.intellij.plugin.util.IntelliJApiHelper -import org.utbot.intellij.plugin.util.PluginJdkInfoProvider -import org.utbot.intellij.plugin.util.signature -import org.utbot.summary.summarize +import org.utbot.intellij.plugin.util.* +import org.utbot.rd.terminateOnException import java.io.File import java.net.URLClassLoader import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.TimeUnit -import org.utbot.engine.util.mockListeners.ForceStaticMockListener -import org.utbot.framework.PathSelectorType -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.JavaDocCommentStyle -import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.services.WorkingDirService -import org.utbot.intellij.plugin.models.packageName -import org.utbot.intellij.plugin.settings.Settings -import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested -import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle -import org.utbot.intellij.plugin.util.PluginWorkingDirProvider -import kotlin.reflect.KClass -import kotlin.reflect.full.functions +import kotlin.io.path.pathString object UtTestsDialogProcessor { @@ -123,154 +111,148 @@ object UtTestsDialogProcessor { (object : Task.Backgroundable(project, "Generate tests") { override fun run(indicator: ProgressIndicator) { - val startTime = System.currentTimeMillis() - val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout) - val totalTimeout = model.timeout * model.srcClasses.size - - indicator.isIndeterminate = false - indicator.text = "Generate tests: read classes" - - val timerHandler = AppExecutorUtil.getAppScheduledExecutorService().scheduleWithFixedDelay({ - indicator.fraction = (System.currentTimeMillis() - startTime).toDouble() / totalTimeout - }, 0, 500, TimeUnit.MILLISECONDS) - - val buildPaths = ReadAction - .nonBlocking { findPaths(model.srcClasses) } - .executeSynchronously() - ?: return - - val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths - val classLoader = urlClassLoader(buildDirs + classpathList) - val context = UtContext(classLoader) - - val testSetsByClass = mutableMapOf>() - val psi2KClass = mutableMapOf>() - var processedClasses = 0 - val totalClasses = model.srcClasses.size - - configureML() - - val testCaseGenerator = TestCaseGenerator( - buildDirs.map { pathStr -> Paths.get(pathStr) }, - classpath, - pluginJarsPath.joinToString(separator = File.pathSeparator), - JdkInfoService.provide(), - isCanceled = { indicator.isCanceled }) - - for (srcClass in model.srcClasses) { - val (methods, className) = ReadAction.nonBlocking, String?>> { - val canonicalName = srcClass.canonicalName - val clazz = classLoader.loadClass(canonicalName).kotlin - psi2KClass[srcClass] = clazz - - val srcMethods = if (model.extractMembersFromSrcClasses) { - val chosenMethods = model.selectedMembers.filter { it.member is PsiMethod } - val chosenNestedClasses = model.selectedMembers.mapNotNull { it.member as? PsiClass } - chosenMethods + chosenNestedClasses.flatMap { - it.extractClassMethodsIncludingNested(false) - } - } else { - srcClass.extractClassMethodsIncludingNested(false) - } - DumbService.getInstance(project).runReadActionInSmartMode(Computable { - clazz.allNestedClasses.flatMap { - findMethodsInClassMatchingSelected(it, srcMethods) - } - }) to srcClass.name - }.executeSynchronously() - - if (methods.isEmpty()) { - logger.error { "No methods matching selected found in class $className." } - continue - } - - indicator.text = "Generate test cases for class $className" - if (totalClasses > 1) { - indicator.fraction = - indicator.fraction.coerceAtLeast(0.9 * processedClasses / totalClasses) + val ldef = LifetimeDefinition() + ldef.terminateOnException { lifetime -> + val startTime = System.currentTimeMillis() + val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout) + val totalTimeout = model.timeout * model.srcClasses.size + + indicator.isIndeterminate = false + indicator.text = "Generate tests: read classes" + + val timerHandler = + AppExecutorUtil.getAppScheduledExecutorService().scheduleWithFixedDelay({ + indicator.fraction = + (System.currentTimeMillis() - startTime).toDouble() / totalTimeout + }, 0, 500, TimeUnit.MILLISECONDS) + + val buildPaths = ReadAction + .nonBlocking { findPaths(model.srcClasses) } + .executeSynchronously() + ?: return + + val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths + + val testSetsByClass = mutableMapOf() + val psi2KClass = mutableMapOf() + var processedClasses = 0 + val totalClasses = model.srcClasses.size + + val proc = EngineProcess(lifetime) + + proc.setupUtContext(buildDirs + classpathList) + proc.createTestGenerator( + buildDirs, + classpath, + pluginJarsPath.joinToString(separator = File.pathSeparator), + JdkInfoService.provide() + ) { + ApplicationManager.getApplication().runReadAction(Computable { + indicator.isCanceled + }) } - // set timeout for concrete execution and for generated tests - UtSettings.concreteExecutionTimeoutInChildProcess = model.hangingTestsTimeout.timeoutMs - - UtSettings.useCustomJavaDocTags = model.commentStyle == JavaDocCommentStyle.CUSTOM_JAVADOC_TAGS - - val searchDirectory = ReadAction - .nonBlocking { - project.basePath?.let { Paths.get(it) } - ?: Paths.get(srcClass.containingFile.virtualFile.parent.path) + for (srcClass in model.srcClasses) { + val (methods, className) = ReadAction.nonBlocking, String?>> { + val canonicalName = srcClass.canonicalName + val classId = proc.obtainClassId(canonicalName) + psi2KClass[srcClass] = classId + + val srcMethods = if (model.extractMembersFromSrcClasses) { + val chosenMethods = model.selectedMembers.filter { it.member is PsiMethod } + val chosenNestedClasses = + model.selectedMembers.mapNotNull { it.member as? PsiClass } + chosenMethods + chosenNestedClasses.flatMap { + it.extractClassMethodsIncludingNested(false) + } + } else { + srcClass.extractClassMethodsIncludingNested(false) + } + DumbService.getInstance(project).runReadActionInSmartMode(Computable { + proc.findMethodsInClassMatchingSelected(classId, srcMethods) + }) to srcClass.name + }.executeSynchronously() + + if (methods.isEmpty()) { + logger.error { "No methods matching selected found in class $className." } + continue } - .executeSynchronously() - - withStaticsSubstitutionRequired(true) { - val mockFrameworkInstalled = model.mockFramework?.isInstalled ?: true - if (!mockFrameworkInstalled) { - ForceMockListener.create(testCaseGenerator, model.conflictTriggers) + indicator.text = "Generate test cases for class $className" + if (totalClasses > 1) { + indicator.fraction = + indicator.fraction.coerceAtLeast(0.9 * processedClasses / totalClasses) } - if (!model.staticsMocking.isConfigured) { - ForceStaticMockListener.create(testCaseGenerator, model.conflictTriggers) - } + // set timeout for concrete execution and for generated tests + UtSettings.concreteExecutionTimeoutInChildProcess = + model.hangingTestsTimeout.timeoutMs - val notEmptyCases = runCatching { - withUtContext(context) { - testCaseGenerator - .generate( - methods, - model.mockStrategy, - model.chosenClassesToMockAlways, - model.timeout, - generate = testFlow { - generationTimeout = model.timeout - isSymbolicEngineEnabled = true - isFuzzingEnabled = UtSettings.useFuzzing - fuzzingValue = project.service().fuzzingValue - } - ) - .map { it.summarize(searchDirectory) } - .filterNot { it.executions.isEmpty() && it.errors.isEmpty() } - } - }.getOrDefault(listOf()) + UtSettings.useCustomJavaDocTags = + model.commentStyle == JavaDocCommentStyle.CUSTOM_JAVADOC_TAGS - if (notEmptyCases.isEmpty()) { - if (model.srcClasses.size > 1) { - logger.error { "Failed to generate any tests cases for class $className" } + val searchDirectory = ReadAction + .nonBlocking { + project.basePath?.let { Paths.get(it) } + ?: Paths.get(srcClass.containingFile.virtualFile.parent.path) + } + .executeSynchronously() + + withStaticsSubstitutionRequired(true) { + val mockFrameworkInstalled = model.mockFramework?.isInstalled ?: true + + val rdGenerateResult = proc.generate( + mockFrameworkInstalled, + model.staticsMocking.isConfigured, + model.conflictTriggers, + methods, + model.mockStrategy, + model.chosenClassesToMockAlways, + model.timeout, + model.timeout, + true, + UtSettings.useFuzzing, + project.service().fuzzingValue, + searchDirectory.pathString + ) + + if (rdGenerateResult.notEmptyCases == 0) { + if (model.srcClasses.size > 1) { + logger.error { "Failed to generate any tests cases for class $className" } + } else { + showErrorDialogLater( + model.project, + errorMessage(className, secondsTimeout), + title = "Failed to generate unit tests for class $className" + ) + } } else { - showErrorDialogLater( - model.project, - errorMessage(className, secondsTimeout), - title = "Failed to generate unit tests for class $className" - ) + testSetsByClass[srcClass] = rdGenerateResult } - } else { - testSetsByClass[srcClass] = notEmptyCases - } - timerHandler.cancel(true) + timerHandler.cancel(true) + } + processedClasses++ } - processedClasses++ - } - if (processedClasses == 0) { - invokeLater { - Messages.showInfoMessage( - model.project, - "No methods for test generation were found among selected items", - "No methods found" - ) + if (processedClasses == 0) { + invokeLater { + Messages.showInfoMessage( + model.project, + "No methods for test generation were found among selected items", + "No methods found" + ) + } + return } - return - } - indicator.fraction = indicator.fraction.coerceAtLeast(0.9) - indicator.text = "Generate code for tests" - // Commented out to generate tests for collected executions even if action was canceled. - // indicator.checkCanceled() + indicator.fraction = indicator.fraction.coerceAtLeast(0.9) + indicator.text = "Generate code for tests" + // Commented out to generate tests for collected executions even if action was canceled. + // indicator.checkCanceled() - invokeLater { - withUtContext(context) { - generateTests(model, testSetsByClass, psi2KClass) + invokeLater { + generateTests(model, testSetsByClass, psi2KClass, proc) } } } @@ -291,49 +273,6 @@ object UtTestsDialogProcessor { } } - /** - * Configures utbot-analytics models for the better path selection. - * - * NOTE: If analytics configuration for the NN Path Selector could not be loaded, - * it switches to the [PathSelectorType.INHERITORS_SELECTOR]. - */ - private fun configureML() { - logger.info { "PathSelectorType: ${UtSettings.pathSelectorType}" } - - if (UtSettings.pathSelectorType == PathSelectorType.ML_SELECTOR) { - val analyticsConfigurationClassPath = UtSettings.analyticsConfigurationClassPath - tryToSetUpMLSelector(analyticsConfigurationClassPath) - } - - if (UtSettings.pathSelectorType == PathSelectorType.TORCH_SELECTOR) { - val analyticsConfigurationClassPath = UtSettings.analyticsTorchConfigurationClassPath - tryToSetUpMLSelector(analyticsConfigurationClassPath) - } - } - - private fun tryToSetUpMLSelector(analyticsConfigurationClassPath: String) { - try { - Class.forName(analyticsConfigurationClassPath) - Predictors.stateRewardPredictor = EngineAnalyticsContext.mlPredictorFactory() - - logger.info { "RewardModelPath: ${UtSettings.modelPath}" } - } catch (e: ClassNotFoundException) { - logger.error { - "Configuration of the predictors from the utbot-analytics module described in the class: " + - "$analyticsConfigurationClassPath is not found!" - } - - logger.info(e) { - "Error while initialization of ${UtSettings.pathSelectorType}. Changing pathSelectorType on INHERITORS_SELECTOR" - } - UtSettings.pathSelectorType = PathSelectorType.INHERITORS_SELECTOR - } - catch (e: Exception) { // engine not found, for example - logger.error { e.message } - UtSettings.pathSelectorType = PathSelectorType.INHERITORS_SELECTOR - } - } - private fun errorMessage(className: String?, timeout: Long) = buildString { appendLine("UtBot failed to generate any test cases for class $className.") appendLine() @@ -341,16 +280,6 @@ object UtTestsDialogProcessor { appendLine("Alternatively, you could try to increase current timeout $timeout sec for generating tests in generation dialog.") } - private fun findMethodsInClassMatchingSelected( - clazz: KClass<*>, - selectedMethods: List - ): List { - val selectedSignatures = selectedMethods.map { it.signature() } - return clazz.functions - .sortedWith(compareBy { selectedSignatures.indexOf(it.signature()) }) - .filter { it.signature().normalized() in selectedSignatures } - .map { it.executableId } - } private fun urlClassLoader(classpath: List) = URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray()) 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 new file mode 100644 index 0000000000..18d25b5ebf --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -0,0 +1,381 @@ +package org.utbot.intellij.plugin.process + +import com.intellij.ide.plugins.cl.PluginClassLoader +import com.intellij.openapi.application.ApplicationManager +import com.intellij.psi.PsiMethod +import com.intellij.refactoring.util.classMembers.MemberInfo +import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rd.util.lifetime.onTermination +import com.jetbrains.rd.util.lifetime.throwIfNotAlive +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import mu.KotlinLogging +import org.jetbrains.kotlin.scripting.resolve.classId +import org.utbot.common.AbstractSettings +import org.utbot.common.getPid +import org.utbot.common.utBotTempDirectory +import org.utbot.framework.UtSettings +import org.utbot.framework.codegen.* +import org.utbot.framework.codegen.model.UtilClassKind +import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport +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.generated.* +import org.utbot.framework.process.generated.Signature +import org.utbot.framework.util.Conflict +import org.utbot.framework.util.ConflictTriggers +import org.utbot.instrumentation.Settings +import org.utbot.instrumentation.util.KryoHelper +import org.utbot.intellij.plugin.models.GenerateTestsModel +import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener +import org.utbot.intellij.plugin.util.signature +import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.rdPortArgument +import org.utbot.rd.startUtProcessWithRdServer +import org.utbot.sarif.SourceFindingStrategy +import java.io.File +import java.nio.file.Path +import java.util.* +import kotlin.io.path.pathString +import kotlin.random.Random +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.memberProperties + +private val logger = KotlinLogging.logger {} +private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs") + +data class RdGTestenerationResult(val notEmptyCases: Int, val testSetsId: Long) + +class EngineProcess(val lifetime: Lifetime) { + private val id = Random.nextLong() + private var count = 0 + + companion object { + private var configPath: Path? = null + private fun getOrCreateLogConfig(): String { + var realPath = configPath + if (realPath == null) { + synchronized(this) { + realPath = configPath + if (realPath == null) { + utBotTempDirectory.toFile().mkdirs() + configPath = utBotTempDirectory.toFile().resolve("EngineProcess_log4j2.xml").apply { + writeText( + """ + + + + + + + + + + + + +""" + ) + }.toPath() + realPath = configPath + } + } + } + return realPath!!.pathString + } + } + // because we cannot load idea bundled lifetime or it will break everything + + private fun debugArgument(): String { + return "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005".takeIf { Settings.runIdeaProcessWithDebug } + ?: "" + } + + private val kryoHelper = KryoHelper(lifetime) + + private suspend fun engineModel(): EngineProcessModel { + lifetime.throwIfNotAlive() + return lock.withLock { + var proc = current + + if (proc == null) { + proc = startUtProcessWithRdServer(lifetime) { port -> + val current = JdkInfoDefaultProvider().info + val required = JdkInfoService.jdkInfoProvider.info + val java = + JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}javaw").toString() + val cp = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( + separator = ";", + prefix = "\"", + postfix = "\"" + ) + val classname = "org.utbot.framework.process.EngineMainKt" + val javaVersionSpecificArguments = + listOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED", "--illegal-access=warn") + .takeIf { JdkInfoService.provide().version > 8 } + ?: emptyList() + 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 + 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 + } + + proc.protocol.engineProcessModel + } + } + + private val lock = Mutex() + private var current: ProcessWithRdServer? = null + + fun setupUtContext(classpathForUrlsClassloader: List) = runBlocking { + engineModel().setupUtContext.startSuspending(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 + fun createTestGenerator( + buildDir: List, + classPath: String?, + dependencyPaths: String, + jdkInfo: JdkInfo, + isCancelled: (Unit) -> Boolean + ) = runBlocking { + engineModel().isCancelled.set(handler = isCancelled) + engineModel().createTestGenerator.startSuspending( + lifetime, + TestGeneratorParams(buildDir.toTypedArray(), classPath, dependencyPaths, JdkInfo(jdkInfo.path.pathString, jdkInfo.version)) + ) + } + + fun obtainClassId(canonicalName: String): ClassId = runBlocking { + kryoHelper.readObject(engineModel().obtainClassId.startSuspending(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 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 + ) + } + + private fun MemberInfo.paramNames(): List = + (this.member as PsiMethod).parameterList.parameters.map { it.name } + + fun generate( + mockInstalled: Boolean, + staticsMockingIsConfigured: Boolean, + conflictTriggers: ConflictTriggers, + methods: List, + mockStrategyApi: MockStrategyApi, + chosenClassesToMockAlways: Set, + timeout: Long, + generationTimeout: Long, + isSymbolicEngineEnabled: Boolean, + isFuzzingEnabled: Boolean, + fuzzingValue: Double, + searchDirectory: String + ): RdGTestenerationResult = runBlocking { + val result = engineModel().generate.startSuspending( + lifetime, + GenerateParams( + mockInstalled, + staticsMockingIsConfigured, + kryoHelper.writeObject(conflictTriggers.toMutableMap()), + kryoHelper.writeObject(methods), + mockStrategyApi.name, + kryoHelper.writeObject(chosenClassesToMockAlways), + timeout, + generationTimeout, + isSymbolicEngineEnabled, + isFuzzingEnabled, + fuzzingValue, + searchDirectory + ) + ) + + return@runBlocking RdGTestenerationResult(result.notEmptyCases, result.testSetsId) + } + + fun render( + testSetsId: Long, + classUnderTest: ClassId, + paramNames: MutableMap>, + generateUtilClassFile: Boolean, + testFramework: TestFramework, + mockFramework: MockFramework, + staticsMocking: StaticsMocking, + forceStaticMocking: ForceStaticMocking, + generateWarningsForStaticsMocking: Boolean, + codegenLanguage: CodegenLanguage, + parameterizedTestSource: ParametrizedTestSource, + runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour, + hangingTestSource: HangingTestsTimeout, + enableTestsTimeout: Boolean, + testClassPackageName: String + ): Pair = runBlocking { + val result = engineModel().render.startSuspending( + lifetime, 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 kryoHelper.readObject(result.utilClassKind) + } + + fun forceTermination() = runBlocking { + engineModel().stopProcess.start(Unit) + current?.terminate() + engineModel().writeSarifReport + } + + fun writeSarif(reportFilePath: Path, + testSetsId: Long, + generatedTestsCode: String, + sourceFindingStrategy: SourceFindingStrategy + ) = runBlocking { + current!!.protocol.rdSourceFindingStrategy.let { + it.getSourceFile.set { params -> + ApplicationManager.getApplication().runReadAction { + sourceFindingStrategy.getSourceFile( + params.classFqn, + params.extension + )?.canonicalPath + } + } + it.getSourceRelativePath.set { params -> + ApplicationManager.getApplication().runReadAction { + sourceFindingStrategy.getSourceRelativePath( + params.classFqn, + params.extension + ) + } + } + it.testsRelativePath.set { _ -> + ApplicationManager.getApplication().runReadAction { + sourceFindingStrategy.testsRelativePath + } + } + } + engineModel().writeSarifReport.startSuspending(WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode)) + } + + 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.triggered + ) + ) + + return@runBlocking Triple(result.notifyMessage, result.statistics, result.hasWarnings) + } + + init { + lifetime.onTermination { + current?.terminate() + } + } +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt index 9eac5d2031..b7077863aa 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt @@ -1,11 +1,12 @@ package org.utbot.intellij.plugin.sarif import org.utbot.common.PathUtil.classFqnToPath -import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath -import org.utbot.sarif.SarifReport import com.intellij.openapi.vfs.VfsUtil +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.utbot.framework.plugin.api.ClassId import org.utbot.intellij.plugin.models.GenerateTestsModel +import org.utbot.intellij.plugin.process.EngineProcess object SarifReportIdea { @@ -14,23 +15,22 @@ object SarifReportIdea { * saves it to test resources directory and notifies the user about the creation. */ fun createAndSave( + proc: EngineProcess, + testSetsId: Long, + classId: ClassId, model: GenerateTestsModel, - testSets: List, generatedTestsCode: String, sourceFinding: SourceFindingStrategyIdea ) { // building the path to the report file - val classFqn = testSets.firstOrNull()?.method?.classId?.name ?: return + val classFqn = classId.name val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) val reportFilePath = sarifReportsPath.resolve("${classFqnToPath(classFqn)}Report.sarif") // creating report related directory - VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) + runWriteAction { VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) } - // creating & saving sarif report - reportFilePath - .toFile() - .writeText(SarifReport(testSets, generatedTestsCode, sourceFinding).createReport()) + proc.writeSarif(reportFilePath, testSetsId, generatedTestsCode, sourceFinding) } } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt index 7deea934ea..4eaff986de 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt @@ -154,7 +154,7 @@ private const val ACTION_GENERATE_AND_RUN = "Generate and Run" class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(model.project) { companion object { const val minSupportedSdkVersion = 8 - const val maxSupportedSdkVersion = 11 + const val maxSupportedSdkVersion = 17 } private val membersTable = MemberSelectionTable(emptyList(), null) @@ -336,7 +336,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m addToLeft(JBLabel().apply { icon = AllIcons.Ide.FatalError text = if (sdkVersion != null) { - "SDK version $sdkVersion is not supported, use ${JavaSdkVersion.JDK_1_8} or ${JavaSdkVersion.JDK_11}." + "SDK version $sdkVersion is not supported, use ${JavaSdkVersion.JDK_1_8}, ${JavaSdkVersion.JDK_11} or ${JavaSdkVersion.JDK_17}" } else { "SDK is not defined" } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IntelliJApiHelper.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IntelliJApiHelper.kt index fa2e6624e8..68d50c3079 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IntelliJApiHelper.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IntelliJApiHelper.kt @@ -9,8 +9,6 @@ import com.intellij.util.PlatformUtils import com.intellij.util.ReflectionUtil import com.intellij.util.concurrency.AppExecutorUtil import org.jetbrains.kotlin.idea.util.application.invokeLater -import org.utbot.framework.plugin.api.util.UtContext -import org.utbot.framework.plugin.api.util.withUtContext /** * This object is required to encapsulate Android API usage and grant safe access to it. @@ -20,21 +18,19 @@ object IntelliJApiHelper { enum class Target { THREAD_POOL, READ_ACTION, WRITE_ACTION, EDT_LATER } fun run(target: Target, runnable: Runnable) { - UtContext.currentContext()?.let { - when (target) { - Target.THREAD_POOL -> AppExecutorUtil.getAppExecutorService().submit { - withUtContext(it) { - runnable.run() - } - } - Target.READ_ACTION -> runReadAction { withUtContext(it) { runnable.run() } } - Target.WRITE_ACTION -> runWriteAction { withUtContext(it) { runnable.run() } } - Target.EDT_LATER -> invokeLater { withUtContext(it) { runnable.run() } } + when (target) { + Target.THREAD_POOL -> AppExecutorUtil.getAppExecutorService().submit { + runnable.run() } - } ?: error("No context in thread ${Thread.currentThread()}") + + Target.READ_ACTION -> runReadAction { runnable.run() } + Target.WRITE_ACTION -> runWriteAction { runnable.run() } + Target.EDT_LATER -> invokeLater { runnable.run() } + } } - private val isAndroidPluginAvailable: Boolean = !PluginManagerCore.isDisabled(PluginId.getId("org.jetbrains.android")) + private val isAndroidPluginAvailable: Boolean = + !PluginManagerCore.isDisabled(PluginId.getId("org.jetbrains.android")) fun isAndroidStudio(): Boolean = isAndroidPluginAvailable && ("AndroidStudio" == PlatformUtils.getPlatformPrefix()) @@ -43,9 +39,9 @@ object IntelliJApiHelper { if (!isAndroidPluginAvailable) return null try { val finderClass = Class.forName("com.android.tools.idea.gradle.util.GradleProjectSettingsFinder") - var method = ReflectionUtil.getMethod(finderClass, "findGradleProjectSettings", Project::class.java)?: return null - val gradleProjectSettings = method.invoke(project)?: return null - method = ReflectionUtil.getMethod(gradleProjectSettings.javaClass, "getGradleJvm")?: return null + var method = ReflectionUtil.getMethod(finderClass, "findGradleProjectSettings", Project::class.java) ?: return null + val gradleProjectSettings = method.invoke(project) ?: return null + method = ReflectionUtil.getMethod(gradleProjectSettings.javaClass, "getGradleJvm") ?: return null val gradleJvm = method.invoke(gradleProjectSettings) return if (gradleJvm is String) gradleJvm else null } catch (e: Exception) { diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/RunConfigurationHelper.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/RunConfigurationHelper.kt index cc0035ca8f..19b8b44615 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/RunConfigurationHelper.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/RunConfigurationHelper.kt @@ -22,8 +22,8 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.SmartPsiElementPointer +import com.intellij.psi.util.childrenOfType import mu.KotlinLogging -import org.jetbrains.plugins.groovy.lang.psi.util.childrenOfType import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.util.IntelliJApiHelper.run diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SignaturesHelper.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SignaturesHelper.kt index 7d6d925b99..8cf49a8af5 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SignaturesHelper.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SignaturesHelper.kt @@ -1,13 +1,8 @@ package org.utbot.intellij.plugin.util -import com.intellij.openapi.project.DumbService -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Computable import com.intellij.psi.PsiMethod import com.intellij.refactoring.util.classMembers.MemberInfo -import kotlin.reflect.KFunction -import kotlin.reflect.KParameter -import kotlin.reflect.jvm.javaType +import org.utbot.framework.plugin.api.Signature fun MemberInfo.signature(): Signature = (this.member as PsiMethod).signature() @@ -17,16 +12,4 @@ private fun PsiMethod.signature() = it.type.canonicalText .replace("...", "[]") //for PsiEllipsisType .replace(",", ", ") // to fix cases like Pair -> Pair - }) - -fun KFunction<*>.signature() = - Signature(this.name, this.parameters.filter { it.kind == KParameter.Kind.VALUE }.map { it.type.javaType.typeName }) - -data class Signature(val name: String, val parameterTypes: List) { - - fun normalized() = this.copy( - parameterTypes = parameterTypes.map { - it?.replace("$", ".") // normalize names of nested classes - } - ) -} \ No newline at end of file + }) \ No newline at end of file diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt index 18598a6c82..3cd74bb432 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/ContestEstimator.kt @@ -14,6 +14,7 @@ import org.utbot.analytics.EngineAnalyticsContext import org.utbot.analytics.Predictors import org.utbot.common.FileUtil import org.utbot.common.bracket +import org.utbot.common.getPid import org.utbot.common.info import org.utbot.contest.Paths.classesLists import org.utbot.contest.Paths.dependenciesJars @@ -200,7 +201,7 @@ enum class Tool { logger.info { "Started processing $classFqn" } val process = ProcessBuilder(command).redirectErrorStream(true).start() - logger.info { "Pid: ${process.pid()}" } + logger.info { "Pid: ${process.getPid}" } process.inputStream.bufferedReader().use { reader -> while (true) { diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 5a944183dc..ea439435b7 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -47,6 +47,7 @@ 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' @@ -98,12 +99,12 @@ test { systemProperty("PROCESS_WITH_RD_SERVER_MOCK", processWithRdServerMockJar.archiveFile.get().getAsFile().canonicalPath) } -task generateProtocolModels(type: RdGenTask) { +task generateChildProcessProtocolModels(type: RdGenTask) { def currentProjectDir = project.projectDir def instrumentationProjectDir = project.rootProject.childProjects["utbot-instrumentation"].projectDir - def hashDir = new File(instrumentationProjectDir, "build/rdgen/hashes/models") - def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") def generatedOutputDir = new File(instrumentationProjectDir, "src/main/kotlin/org/utbot/instrumentation/rd/generated") + def hashDir = generatedOutputDir + def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") def rdParams = extensions.getByName("params") as RdGenExtension group = "rdgen" @@ -116,9 +117,67 @@ task generateProtocolModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.ProtocolRoot" + root = "org.utbot.rd.models.ChildProcessProtocolRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.instrumentation.rd.generated" } +} + +task generateEngineProcessProtocolModels(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") + def hashDir = generatedOutputDir + def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") + def rdParams = extensions.getByName("params") as RdGenExtension + + group = "rdgen" + rdParams.verbose = true + rdParams.sources(sourcesDir) + rdParams.hashFolder = hashDir.canonicalPath + // where to search roots + rdParams.packages = "org.utbot.rd.models" + + 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" + + directory = generatedOutputDir.canonicalPath + namespace = "org.utbot.framework.process.generated" + } +} + +task generateSynchronizationModels(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") + def hashDir = generatedOutputDir + def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") + def rdParams = extensions.getByName("params") as RdGenExtension + + group = "rdgen" + rdParams.verbose = true + rdParams.sources(sourcesDir) + rdParams.hashFolder = hashDir.canonicalPath + // where to search roots + rdParams.packages = "org.utbot.rd.models" + + rdParams.generator { + language = "kotlin" + transform = "symmetric" + root = "org.utbot.rd.models.SynchronizationModelRoot" + + directory = generatedOutputDir.canonicalPath + namespace = "org.utbot.rd.generated" + } } \ 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 new file mode 100644 index 0000000000..6609dd9c2f --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt @@ -0,0 +1,173 @@ +package org.utbot.rd + +import com.jetbrains.rd.framework.* +import com.jetbrains.rd.framework.impl.RdCall +import com.jetbrains.rd.framework.util.launch +import com.jetbrains.rd.util.getLogger +import com.jetbrains.rd.util.info +import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import com.jetbrains.rd.util.lifetime.isAlive +import com.jetbrains.rd.util.lifetime.plusAssign +import com.jetbrains.rd.util.threading.SingleThreadScheduler +import com.jetbrains.rd.util.trace +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeoutOrNull +import org.utbot.common.* +import org.utbot.rd.generated.synchronizationModel +import java.io.File +import kotlin.time.Duration + +const val rdProcessDirName = "rdProcessSync" +val processSyncDirectory = File(utBotTempDirectory.toFile(), rdProcessDirName) +const val rdPortProcessArgumentTag = "rdPort" +internal const val fileWaitTimeoutMillis = 10L +private val logger = getLogger() + +internal fun childCreatedFileName(port: Int): String { + return "$port.created" +} + +internal fun signalChildReady(port: Int) { + processSyncDirectory.mkdirs() + + val signalFile = File(processSyncDirectory, childCreatedFileName(port)) + + if (signalFile.exists()) { + signalFile.delete() + } + + val created = signalFile.createNewFile() + + if (!created) { + throw IllegalStateException("cannot create signal file") + } +} + +fun rdPortArgument(port: Int): String { + return "$rdPortProcessArgumentTag=$port" +} + +fun findRdPort(args: Array): Int { + return args.find { it.startsWith(rdPortProcessArgumentTag) } + ?.run { split("=").last().toInt().coerceIn(1..65535) } + ?: throw IllegalArgumentException("No port provided") +} + +class CallsSynchronizer(private val ldef: LifetimeDefinition, val timeout: Duration) { + private enum class State { + STARTED, + ENDED + } + + private val synchronizer: Channel = Channel(1) + + fun measureExecutionForTermination(block: () -> T): T = runBlocking { + try { + synchronizer.send(State.STARTED) + return@runBlocking block() + } finally { + synchronizer.send(State.ENDED) + } + } + + fun measureExecutionForTermination(call: RdCall, block: (T) -> R) { + call.set { it -> + runBlocking { + measureExecutionForTermination { + block(it) + } + } + } + } + + suspend fun setupTimeout() { + ldef.launch { + var lastState = State.ENDED + while (ldef.isAlive) { + val current: State? = + withTimeoutOrNull(timeout) { + synchronizer.receive() + } + if (current == null) { + if (lastState == State.ENDED) { + // process is waiting for command more than expected, better die + logger.info { "terminating lifetime by timeout" } + stopProtocol() + break + } + } else { + lastState = current + } + } + } + } + + fun stopProtocol() { + ldef.terminate() + } +} + +class ClientProtocolBuilder { + private var timeout = Duration.INFINITE + + suspend fun start(port: Int, parent: Lifetime? = null, bindables: Protocol.(CallsSynchronizer) -> Unit) { + val pid = currentProcessPid.toInt() + val ldef = parent?.createNested() ?: LifetimeDefinition() + ldef.terminateOnException { _ -> + ldef += { logger.info { "lifetime terminated" } } + ldef += { + val syncFile = File(processSyncDirectory, childCreatedFileName(port)) + + if (syncFile.exists()) { + logger.info { "sync file existed" } + syncFile.delete() + } + } + logger.info { "pid - $pid, port - $port" } + logger.info { "isJvm8 - $isJvm8, isJvm9Plus - $isJvm9Plus, isWindows - $isWindows" } + + val name = "Client$port" + val rdClientProtocolScheduler = SingleThreadScheduler(ldef, "Scheduler for $name") + val clientProtocol = Protocol( + name, + Serializers(), + Identities(IdKind.Client), + rdClientProtocolScheduler, + SocketWire.Client(ldef, rdClientProtocolScheduler, port), + ldef + ) + val synchronizer = CallsSynchronizer(ldef, timeout) + + synchronizer.setupTimeout() + rdClientProtocolScheduler.pump(ldef) { + clientProtocol.synchronizationModel + clientProtocol.bindables(synchronizer) + } + + signalChildReady(port) + logger.info { "signalled" } + clientProtocol.synchronizationModel.synchronizationSignal.let { sync -> + val answerFromMainProcess = sync.adviseForConditionAsync(ldef) { + if (it == "main") { + logger.trace { "received from main" } + synchronizer.measureExecutionForTermination { + sync.fire("child") + } + true + } else { + false + } + } + answerFromMainProcess.await() + } + ldef.awaitTermination() + } + } + + fun withProtocolTimeout(duration: Duration): ClientProtocolBuilder { + timeout = duration + return this + } +} \ No newline at end of file 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 93e6dba94d..fb1241a617 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/LifetimedProcess.kt @@ -55,8 +55,8 @@ inline fun R.terminateOnException(block: (R) -> T): T { } } -const val processKillTimeoutMillis = 100L -const val checkProcessAliveDelay = 100L +private const val processKillTimeoutMillis = 100L +private const val checkProcessAliveDelayMillis = 100L class LifetimedProcessIml(override val process: Process, lifetime: Lifetime? = null): LifetimedProcess { private val ldef: LifetimeDefinition @@ -69,12 +69,12 @@ class LifetimedProcessIml(override val process: Process, lifetime: Lifetime? = n ldef.onTermination { process.destroy() - if (process.waitFor(processKillTimeoutMillis, TimeUnit.MILLISECONDS)) + if (!process.waitFor(processKillTimeoutMillis, TimeUnit.MILLISECONDS)) process.destroyForcibly() } UtRdCoroutineScope.current.launch(ldef) { while (process.isAlive) { - delay(checkProcessAliveDelay) + delay(checkProcessAliveDelayMillis) } ldef.terminate() 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 cf475e345a..31834ca95b 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ProcessWithRdServer.kt @@ -1,10 +1,19 @@ package org.utbot.rd +import com.jetbrains.rd.framework.IProtocol import com.jetbrains.rd.framework.Protocol import com.jetbrains.rd.framework.serverPort import com.jetbrains.rd.framework.util.NetUtils +import com.jetbrains.rd.util.getLogger import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rd.util.lifetime.isAlive import com.jetbrains.rd.util.lifetime.throwIfNotAlive +import com.jetbrains.rd.util.trace +import kotlinx.coroutines.delay +import org.utbot.common.getPid +import org.utbot.rd.generated.synchronizationModel +import java.io.File +import java.nio.file.Files /** * Process will be terminated if lifetime is not alive @@ -66,11 +75,16 @@ suspend fun startProcessWithRdServer( * 2. protocol should be bound to process lifetime */ interface ProcessWithRdServer : LifetimedProcess { - val protocol: Protocol + val protocol: IProtocol val port: Int get() = protocol.wire.serverPort + + suspend fun initModels(bindables: Protocol.() -> Unit): ProcessWithRdServer + suspend fun awaitSignal(): ProcessWithRdServer } +private val logger = getLogger() + class ProcessWithRdServerImpl private constructor( private val child: LifetimedProcess, serverFactory: (Lifetime) -> Protocol @@ -84,4 +98,58 @@ class ProcessWithRdServerImpl private constructor( it.apply { protocol.wire.connected.adviseForConditionAsync(lifetime).await() } } } + + 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 + } + processSyncDirectory.mkdirs() + + // there 2 stages at rd protocol initialization: + // 1. we need to bind all entities - for ex. generated model and custom signal + // because we cannot operate with unbound + // 2. we need to wait when all that entities bound on the other side + // because when we fire something that is not bound on another side - we will lose this call + // to guarantee 2nd stage success - there is custom simple synchronization: + // 1. child process will create file "${port}.created" - this indicates that child process is ready to receive messages + // 2. we will test the connection via sync RdSignal + // only then we can successfully start operating + val pid = process.getPid.toInt() + val syncFile = File(processSyncDirectory, childCreatedFileName(port)) + + while (lifetime.isAlive) { + if (Files.deleteIfExists(syncFile.toPath())) { + logger.trace { "process $pid for port $port: signal file deleted connecting" } + break + } + + delay(fileWaitTimeoutMillis) + } + + protocol.synchronizationModel.synchronizationSignal.let { sync -> + val messageFromChild = sync.adviseForConditionAsync(lifetime) { it == "child" } + + while (messageFromChild.isActive) { + sync.fire("main") + delay(10) + } + } + + lifetime.onTermination { + if (syncFile.exists()) { + logger.trace { "process $pid for port $port: on terminating syncFile existed" } + syncFile.delete() + } + } + + return this + } } \ No newline at end of file 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 eea2b0d1d8..15ef69f2f5 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdCoroutineScope.kt @@ -6,7 +6,6 @@ import com.jetbrains.rd.util.lifetime.Lifetime class UtRdCoroutineScope(lifetime: Lifetime) : RdCoroutineScope(lifetime) { companion object { - val scheduler = UtSingleThreadScheduler("UtRdCoroutineScope") val current = UtRdCoroutineScope(Lifetime.Eternal) } @@ -14,5 +13,5 @@ class UtRdCoroutineScope(lifetime: Lifetime) : RdCoroutineScope(lifetime) { override(lifetime, this) } - override val defaultDispatcher = scheduler.asCoroutineDispatcher + override val defaultDispatcher = UtSingleThreadScheduler("UtCoroutineScheduler").asCoroutineDispatcher } \ No newline at end of file diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdKLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdKLogger.kt deleted file mode 100644 index b0dcb70616..0000000000 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdKLogger.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.utbot.rd - -import com.jetbrains.rd.util.ILoggerFactory -import com.jetbrains.rd.util.LogLevel -import com.jetbrains.rd.util.Logger -import com.jetbrains.rd.util.defaultLogFormat -import mu.KLogger -import mu.KotlinLogging - -object UtRdKLoggerFactory : ILoggerFactory { - override fun getLogger(category: String): Logger { - return UtRdKLogger(KotlinLogging.logger(category), category) - } -} - -class UtRdKLogger(private val logger: KLogger, private val category: String) : Logger { - override fun isEnabled(level: LogLevel): Boolean { - return when (level) { - LogLevel.Trace -> logger.isTraceEnabled - LogLevel.Debug -> logger.isDebugEnabled - LogLevel.Info -> logger.isInfoEnabled - LogLevel.Warn -> logger.isWarnEnabled - LogLevel.Error -> logger.isErrorEnabled - LogLevel.Fatal -> logger.isErrorEnabled - } - } - - override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { - val msg = defaultLogFormat(category, level, message, throwable) - when (level) { - LogLevel.Trace -> logger.trace(msg) - LogLevel.Debug -> logger.debug(msg) - LogLevel.Info -> logger.info(msg) - LogLevel.Warn -> logger.warn(msg) - LogLevel.Error -> logger.error(msg) - LogLevel.Fatal -> logger.error(msg) - } - } -} \ No newline at end of file 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 87086fb0b0..a7f9f25d25 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/UtRdUtil.kt @@ -2,14 +2,17 @@ package org.utbot.rd import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.util.NetUtils +import com.jetbrains.rd.framework.util.synchronizeWith import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.LifetimeDefinition import com.jetbrains.rd.util.lifetime.throwIfNotAlive import com.jetbrains.rd.util.reactive.IScheduler import com.jetbrains.rd.util.reactive.ISource +import com.jetbrains.rd.util.threading.SingleThreadScheduler import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred +// useful when initializing something inline fun LifetimeDefinition.terminateOnException(block: (Lifetime) -> T): T { try { return block(this) @@ -19,26 +22,39 @@ inline fun LifetimeDefinition.terminateOnException(block: (Lifetime) -> T): } } -suspend fun IScheduler.pump(lifetime: Lifetime, block: () -> T): T { +// suspends until provided lifetime is terminated or coroutine cancelled +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 +suspend fun IScheduler.pump(lifetime: Lifetime, block: (Lifetime) -> T): T { val ldef = lifetime.createNested() val deferred = CompletableDeferred() - ldef.onTermination { deferred.cancel() } - deferred.invokeOnCompletion { ldef.terminate() } + ldef.synchronizeWith(deferred) this.invokeOrQueue { - deferred.complete(block()) + deferred.complete(block(ldef)) } 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 { val ldef = lifetime.createNested() val deferred = CompletableDeferred() - ldef.onTermination { deferred.cancel() } - deferred.invokeOnCompletion { ldef.terminate() } + ldef.synchronizeWith(deferred) this.advise(ldef) { if(condition(it)) { @@ -65,12 +81,14 @@ suspend fun startUtProcessWithRdServer( val port = NetUtils.findFreePort(0) return factory(port).withRdServer(lifetime) { + val name = "Server$port" + val rdServerProtocolScheduler = SingleThreadScheduler(it, "Scheduler for $name") Protocol( "Server", Serializers(), Identities(IdKind.Server), - UtRdCoroutineScope.scheduler, - SocketWire.Server(it, UtRdCoroutineScope.scheduler, port, "ServerSocket"), + rdServerProtocolScheduler, + SocketWire.Server(it, rdServerProtocolScheduler, port, "ServerSocket"), it ) } 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 new file mode 100644 index 0000000000..dafb2c3887 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt @@ -0,0 +1,94 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") +package org.utbot.rd.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 [SynchronizationModel.kt:7] + */ +class SynchronizationModel private constructor( + private val _synchronizationSignal: RdSignal +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + } + + + @JvmStatic + @JvmName("internalCreateModel") + @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): SynchronizationModel { + @Suppress("DEPRECATION") + return create(lifetime, protocol) + } + + @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) + + return SynchronizationModel().apply { + identify(protocol.identity, RdId.Null.mix("SynchronizationModel")) + bind(lifetime, protocol, "SynchronizationModel") + } + } + + + const val serializationHash = -6677090974058917499L + + } + override val serializersOwner: ISerializersOwner get() = SynchronizationModel + override val serializationHash: Long get() = SynchronizationModel.serializationHash + + //fields + val synchronizationSignal: IAsyncSignal get() = _synchronizationSignal + //methods + //initializer + init { + _synchronizationSignal.async = true + } + + init { + bindableChildren.add("synchronizationSignal" to _synchronizationSignal) + } + + //secondary constructor + private constructor( + ) : this( + RdSignal(FrameworkMarshallers.String) + ) + + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SynchronizationModel (") + printer.indent { + print("synchronizationSignal = "); _synchronizationSignal.print(printer); println() + } + printer.print(")") + } + //deepClone + override fun deepClone(): SynchronizationModel { + return SynchronizationModel( + _synchronizationSignal.deepClonePolymorphic() + ) + } + //contexts +} +val IProtocol.synchronizationModel get() = getOrCreateExtension(SynchronizationModel::class) { @Suppress("DEPRECATION") SynchronizationModel.create(lifetime, this) } + diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModelRoot.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModelRoot.Generated.kt new file mode 100644 index 0000000000..32b75c35ed --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModelRoot.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.rd.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 [SynchronizationModel.kt:5] + */ +class SynchronizationModelRoot private constructor( +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + SynchronizationModelRoot.register(serializers) + SynchronizationModel.register(serializers) + } + + + + + + const val serializationHash = -1304011640135373779L + + } + override val serializersOwner: ISerializersOwner get() = SynchronizationModelRoot + override val serializationHash: Long get() = SynchronizationModelRoot.serializationHash + + //fields + //methods + //initializer + //secondary constructor + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SynchronizationModelRoot (") + printer.print(")") + } + //deepClone + override fun deepClone(): SynchronizationModelRoot { + return SynchronizationModelRoot( + ) + } + //contexts +} 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 new file mode 100644 index 0000000000..6a18fe1544 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt @@ -0,0 +1,33 @@ +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 java.io.PrintStream +import java.time.LocalDateTime + +class UtRdConsoleLogger( + private val loggersLevel: LogLevel, + private val streamToWrite: PrintStream, + private val category: String = "" +) : Logger { + override fun isEnabled(level: LogLevel): Boolean { + return level >= loggersLevel + } + + override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { + if (!isEnabled(level)) + return + + val msg = LocalDateTime.now().format(dateFormatter) + " | ${ + defaultLogFormat( + 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/UtRdConsoleLoggerFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt new file mode 100644 index 0000000000..c34e6a3788 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLoggerFactory.kt @@ -0,0 +1,15 @@ +package org.utbot.rd.loggers + +import com.jetbrains.rd.util.ILoggerFactory +import com.jetbrains.rd.util.LogLevel +import com.jetbrains.rd.util.Logger +import java.io.PrintStream + +class UtRdConsoleLoggerFactory( + private val loggersLevel: LogLevel, + private val streamToWrite: PrintStream +) : ILoggerFactory { + override fun getLogger(category: String): Logger { + return UtRdConsoleLogger(loggersLevel, streamToWrite, category) + } +} \ 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 new file mode 100644 index 0000000000..c72de72d44 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt @@ -0,0 +1,37 @@ +package org.utbot.rd.loggers + +import com.jetbrains.rd.util.LogLevel +import com.jetbrains.rd.util.Logger +import com.jetbrains.rd.util.defaultLogFormat +import mu.KLogger +import org.utbot.common.dateFormatter +import java.time.LocalDateTime + +class UtRdKLogger(private val realLogger: KLogger, private val category: String): Logger { + override fun isEnabled(level: LogLevel): Boolean { + return when (level) { + LogLevel.Trace -> realLogger.isTraceEnabled + LogLevel.Debug -> realLogger.isDebugEnabled + LogLevel.Info -> realLogger.isInfoEnabled + LogLevel.Warn -> realLogger.isWarnEnabled + LogLevel.Error -> realLogger.isErrorEnabled + LogLevel.Fatal -> realLogger.isErrorEnabled + } + } + + override fun log(level: LogLevel, message: Any?, throwable: Throwable?) { + if (!isEnabled(level)) + return + + val msg = LocalDateTime.now().format(dateFormatter) + " | ${defaultLogFormat(category, level, message, throwable)}" + + when (level) { + LogLevel.Trace -> realLogger.trace(msg) + LogLevel.Debug -> realLogger.debug(msg) + LogLevel.Info -> realLogger.info(msg) + LogLevel.Warn -> realLogger.warn(msg) + LogLevel.Error -> realLogger.error(msg) + LogLevel.Fatal -> realLogger.error(msg) + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..4c1f8910e8 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLoggerFactory.kt @@ -0,0 +1,11 @@ +package org.utbot.rd.loggers + +import com.jetbrains.rd.util.ILoggerFactory +import com.jetbrains.rd.util.Logger +import mu.KLogger + +class UtRdKLoggerFactory(private val realLogger: KLogger) : ILoggerFactory { + override fun getLogger(category: String): Logger { + return UtRdKLogger(realLogger, category) + } +} \ No newline at end of file diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt similarity index 96% rename from utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt rename to utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt index b79243d943..3a18853a39 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/ChildProcessModel.kt @@ -2,9 +2,9 @@ package org.utbot.rd.models import com.jetbrains.rd.generator.nova.* -object ProtocolRoot : Root() +object ChildProcessProtocolRoot : Root() -object ProtocolModel : Ext(ProtocolRoot) { +object ChildProcessModel : Ext(ChildProcessProtocolRoot) { val AddPathsParams = structdef { field("pathsToUserClasses", PredefinedType.string) field("pathsToDependencyClasses", PredefinedType.string) 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 new file mode 100644 index 0000000000..d54c9d31ee --- /dev/null +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt @@ -0,0 +1,128 @@ +package org.utbot.rd.models + +import com.jetbrains.rd.generator.nova.* + +object EngineProcessProtocolRoot : Root() + +object RdSourceFindingStrategy : Ext(EngineProcessProtocolRoot) { + val sourceStrategeMethodArgs = structdef { + field("classFqn", PredefinedType.string) + field("extension", PredefinedType.string.nullable) + } + + init { + call("testsRelativePath", PredefinedType.void, PredefinedType.string).async + call("getSourceRelativePath", sourceStrategeMethodArgs, PredefinedType.string).async + call("getSourceFile", sourceStrategeMethodArgs, PredefinedType.string.nullable).async + } +} + +object EngineProcessModel : Ext(EngineProcessProtocolRoot) { + val jdkInfo = structdef { + field("path", PredefinedType.string) + field("version", PredefinedType.int) + } + + val testGeneratorParams = structdef { + field("buildDir", array(PredefinedType.string)) + field("classpath", PredefinedType.string.nullable) + field("dependencyPaths", PredefinedType.string) + field("jdkInfo", jdkInfo) + } + val generateParams = structdef { + // mocks + field("mockInstalled", PredefinedType.bool) + field("staticsMockingIsConfigureda", PredefinedType.bool) + field("conflictTriggers", array(PredefinedType.byte)) + // generate + field("methods", array(PredefinedType.byte)) + field("mockStrategy", PredefinedType.string) + field("chosenClassesToMockAlways", array(PredefinedType.byte)) + field("timeout", PredefinedType.long) + // testflow + field("generationTimeout", PredefinedType.long) + field("isSymbolicEngineEnabled", PredefinedType.bool) + field("isFuzzingEnabled", PredefinedType.bool) + field("fuzzingValue", PredefinedType.double) + // method filters + field("searchDirectory", PredefinedType.string) + } + val generateResult = structdef { + field("notEmptyCases", PredefinedType.int) + field("testSetsId", PredefinedType.long) + } + val renderParams = structdef { + field("testSetsId", PredefinedType.long) + field("classUnderTest", array(PredefinedType.byte)) + field("paramNames", array(PredefinedType.byte)) + field("generateUtilClassFile", PredefinedType.bool) + field("testFramework", PredefinedType.string) + field("mockFramework", PredefinedType.string) + field("codegenLanguage", PredefinedType.string) + field("parameterizedTestSource", PredefinedType.string) + field("staticsMocking", PredefinedType.string) + field("forceStaticMocking", array(PredefinedType.byte)) + field("generateWarningsForStaticMocking", PredefinedType.bool) + field("runtimeExceptionTestsBehaviour", PredefinedType.string) + field("hangingTestsTimeout", PredefinedType.long) + field("enableTestsTimeout", PredefinedType.bool) + field("testClassPackageName", PredefinedType.string) + } + val renderResult = structdef { + field("generatedCode", PredefinedType.string) + field("utilClassKind", array(PredefinedType.byte)) + } + val setupContextParams = structdef { + field("classpathForUrlsClassloader", immutableList(PredefinedType.string)) + } + val signature = structdef { + field("name", PredefinedType.string) + field("parametersTypes", immutableList(PredefinedType.string.nullable)) + } + val findMethodsInClassMatchingSelectedArguments = structdef { + field("classId", array(PredefinedType.byte)) + field("signatures", immutableList(signature)) + } + val findMethodsInClassMatchingSelectedResult = structdef { + field("executableIds", array(PredefinedType.byte)) + } + val findMethodParamNamesArguments = structdef { + field("classId", array(PredefinedType.byte)) + field("bySignature", array(PredefinedType.byte)) + } + val findMethodParamNamesResult = structdef { + field("paramNames", array(PredefinedType.byte)) + } + val writeSarifReportArguments = structdef { + field("testSetsId", PredefinedType.long) + field("reportFilePath", PredefinedType.string) + field("generatedTestsCode", PredefinedType.string) + } + val generateTestReportArgs = structdef { + field("eventLogMessage", PredefinedType.string.nullable) + field("testPackageName", PredefinedType.string.nullable) + field("isMultiPackage", PredefinedType.bool) + field("forceMockWarning", PredefinedType.string.nullable) + field("forceStaticMockWarnings", PredefinedType.string.nullable) + field("testFrameworkWarning", PredefinedType.string.nullable) + field("hasInitialWarnings", PredefinedType.bool) + } + val generateTestReportResult = structdef { + field("notifyMessage", PredefinedType.string) + field("statistics", PredefinedType.string.nullable) + field("hasWarnings", PredefinedType.bool) + } + init { + call("setupUtContext", setupContextParams, PredefinedType.void).async + call("createTestGenerator", testGeneratorParams, PredefinedType.void).async + call("isCancelled", PredefinedType.void, PredefinedType.bool).async + call("generate", generateParams, generateResult).async + call("render", renderParams, renderResult).async + call("stopProcess", PredefinedType.void, PredefinedType.void).async + call("obtainClassId", PredefinedType.string, array(PredefinedType.byte)).async + call("findMethodsInClassMatchingSelected", findMethodsInClassMatchingSelectedArguments, findMethodsInClassMatchingSelectedResult).async + call("findMethodParamNames", findMethodParamNamesArguments, findMethodParamNamesResult).async + call("writeSarifReport", writeSarifReportArguments, PredefinedType.void).async + call("generateTestReport", generateTestReportArgs, generateTestReportResult).async + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..605b573c78 --- /dev/null +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SettingsModel.kt @@ -0,0 +1,18 @@ +package org.utbot.rd.models + +import com.jetbrains.rd.generator.nova.* + +object SettingsProtocolRoot: Root() + +object SettingsModel : Ext(SettingsProtocolRoot) { + val settingForArgument = structdef { + field("key", PredefinedType.string) + field("propertyName", PredefinedType.string) + } + val settingForResult = structdef { + field("value", PredefinedType.string.nullable) + } + init { + call("settingFor", settingForArgument, settingForResult).async + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..4484671419 --- /dev/null +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt @@ -0,0 +1,11 @@ +package org.utbot.rd.models + +import com.jetbrains.rd.generator.nova.* + +object SynchronizationModelRoot: Root() + +object SynchronizationModel: Ext(SynchronizationModelRoot) { + init { + signal("synchronizationSignal", PredefinedType.string).async + } +} \ No newline at end of file