diff --git a/.gitignore b/.gitignore index 7db9331a69..011d499004 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ target/ .idea/ .gradle/ *.log +*.rdgen \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/util/ClassUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/util/ClassUtil.kt index b8bdf1144f..a5858209f7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/util/ClassUtil.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/util/ClassUtil.kt @@ -56,7 +56,7 @@ object ClassUtil { val clazz = classLoader.tryLoadClass(classFqn) ?: return null val sourceFileName = withUtContext(UtContext(classLoader)) { - Instrumenter.computeSourceFileName(clazz) // finds the file name in bytecode + Instrumenter.adapter.computeSourceFileName(clazz) // finds the file name in bytecode } ?: return null val candidates = sourceCodeFiles.filter { sourceCodeFile -> sourceCodeFile.endsWith(File(sourceFileName)) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt index f240015ed5..2d6532d827 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt @@ -23,8 +23,8 @@ 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.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.CallsSynchronizer import org.utbot.rd.ClientProtocolBuilder @@ -33,13 +33,9 @@ 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 @@ -56,6 +52,7 @@ suspend fun main(args: Array) = runBlocking { ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { settingsModel rdSourceFindingStrategy + rdInstrumenterAdapter AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol)) val kryoHelper = KryoHelper(lifetime) @@ -82,6 +79,7 @@ private fun EngineProcessModel.setup( } synchronizer.measureExecutionForTermination(createTestGenerator) { params -> AnalyticsConfigureUtil.configureML() + Instrumenter.adapter = RdInstrumenter(realProtocol) testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, classpath = params.classpath, dependencyPaths = params.dependencyPaths, @@ -150,7 +148,7 @@ private fun EngineProcessModel.setup( codeGenerator.generateAsStringWithTestReport(testSets[testSetsId]!!) .let { testGenerationReports.add(it.testsGenerationReport) - RenderResult(it.generatedCode, kryoHelper.writeObject(it.utilClassKind)) + RenderResult(it.generatedCode, it.utilClassKind?.javaClass?.simpleName) } } synchronizer.measureExecutionForTermination(stopProcess) { synchronizer.stopProtocol() } @@ -177,6 +175,7 @@ private fun EngineProcessModel.setup( } synchronizer.measureExecutionForTermination(writeSarifReport) { params -> val reportFilePath = Paths.get(params.reportFilePath) + reportFilePath.parent.toFile().mkdirs() reportFilePath.toFile().writeText( SarifReport( testSets[params.testSetsId]!!, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt new file mode 100644 index 0000000000..a53c851a5e --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/RdInstrumenter.kt @@ -0,0 +1,36 @@ +package org.utbot.framework.process + +import com.jetbrains.rd.framework.IProtocol +import kotlinx.coroutines.runBlocking +import mu.KotlinLogging +import org.utbot.framework.process.generated.ComputeSourceFileByClassArguments +import org.utbot.framework.process.generated.rdInstrumenterAdapter +import org.utbot.instrumentation.instrumentation.instrumenter.InstrumenterAdapter +import java.io.File +import java.nio.file.Path + +private val logger = KotlinLogging.logger { } + +class RdInstrumenter(private val protocol: IProtocol): InstrumenterAdapter() { + override fun computeSourceFileByClass( + className: String, + packageName: String?, + directoryToSearchRecursively: Path + ): File? = runBlocking { + logger.debug { "starting computeSourceFileByClass with classname - $className" } + val result = try { + protocol.rdInstrumenterAdapter.computeSourceFileByClass.startSuspending( + ComputeSourceFileByClassArguments( + className, + packageName + ) + ) + } + catch(e: Exception) { + logger.error(e) { "error during computeSourceFileByClass" } + throw e + } + logger.debug { "computeSourceFileByClass result for $className from idea: $result"} + return@runBlocking result?.let { File(it) } + } +} \ 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 index 72ba022123..bda4112811 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [EngineProcessModel.kt:20] + * #### Generated from [EngineProcessModel.kt:30] */ class EngineProcessModel private constructor( private val _setupUtContext: RdCall, @@ -73,7 +73,7 @@ class EngineProcessModel private constructor( } - const val serializationHash = 4674749231408610997L + const val serializationHash = 3907671513584285891L } override val serializersOwner: ISerializersOwner get() = EngineProcessModel @@ -180,7 +180,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel /** - * #### Generated from [EngineProcessModel.kt:89] + * #### Generated from [EngineProcessModel.kt:99] */ data class FindMethodParamNamesArguments ( val classId: ByteArray, @@ -243,7 +243,7 @@ data class FindMethodParamNamesArguments ( /** - * #### Generated from [EngineProcessModel.kt:93] + * #### Generated from [EngineProcessModel.kt:103] */ data class FindMethodParamNamesResult ( val paramNames: ByteArray @@ -300,7 +300,7 @@ data class FindMethodParamNamesResult ( /** - * #### Generated from [EngineProcessModel.kt:82] + * #### Generated from [EngineProcessModel.kt:92] */ data class FindMethodsInClassMatchingSelectedArguments ( val classId: ByteArray, @@ -363,7 +363,7 @@ data class FindMethodsInClassMatchingSelectedArguments ( /** - * #### Generated from [EngineProcessModel.kt:86] + * #### Generated from [EngineProcessModel.kt:96] */ data class FindMethodsInClassMatchingSelectedResult ( val executableIds: ByteArray @@ -420,7 +420,7 @@ data class FindMethodsInClassMatchingSelectedResult ( /** - * #### Generated from [EngineProcessModel.kt:32] + * #### Generated from [EngineProcessModel.kt:42] */ data class GenerateParams ( val mockInstalled: Boolean, @@ -543,7 +543,7 @@ data class GenerateParams ( /** - * #### Generated from [EngineProcessModel.kt:50] + * #### Generated from [EngineProcessModel.kt:60] */ data class GenerateResult ( val notEmptyCases: Int, @@ -606,7 +606,7 @@ data class GenerateResult ( /** - * #### Generated from [EngineProcessModel.kt:101] + * #### Generated from [EngineProcessModel.kt:111] */ data class GenerateTestReportArgs ( val eventLogMessage: String?, @@ -699,7 +699,7 @@ data class GenerateTestReportArgs ( /** - * #### Generated from [EngineProcessModel.kt:110] + * #### Generated from [EngineProcessModel.kt:120] */ data class GenerateTestReportResult ( val notifyMessage: String, @@ -768,7 +768,7 @@ data class GenerateTestReportResult ( /** - * #### Generated from [EngineProcessModel.kt:21] + * #### Generated from [EngineProcessModel.kt:31] */ data class JdkInfo ( val path: String, @@ -831,7 +831,7 @@ data class JdkInfo ( /** - * #### Generated from [EngineProcessModel.kt:54] + * #### Generated from [EngineProcessModel.kt:64] */ data class RenderParams ( val testSetsId: Long, @@ -972,11 +972,11 @@ data class RenderParams ( /** - * #### Generated from [EngineProcessModel.kt:71] + * #### Generated from [EngineProcessModel.kt:81] */ data class RenderResult ( val generatedCode: String, - val utilClassKind: ByteArray + val utilClassKind: String? ) : IPrintable { //companion @@ -986,13 +986,13 @@ data class RenderResult ( @Suppress("UNCHECKED_CAST") override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): RenderResult { val generatedCode = buffer.readString() - val utilClassKind = buffer.readByteArray() + val utilClassKind = buffer.readNullable { buffer.readString() } return RenderResult(generatedCode, utilClassKind) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: RenderResult) { buffer.writeString(value.generatedCode) - buffer.writeByteArray(value.utilClassKind) + buffer.writeNullable(value.utilClassKind) { buffer.writeString(it) } } @@ -1009,7 +1009,7 @@ data class RenderResult ( other as RenderResult if (generatedCode != other.generatedCode) return false - if (!(utilClassKind contentEquals other.utilClassKind)) return false + if (utilClassKind != other.utilClassKind) return false return true } @@ -1017,7 +1017,7 @@ data class RenderResult ( override fun hashCode(): Int { var __r = 0 __r = __r*31 + generatedCode.hashCode() - __r = __r*31 + utilClassKind.contentHashCode() + __r = __r*31 + if (utilClassKind != null) utilClassKind.hashCode() else 0 return __r } //pretty print @@ -1035,7 +1035,7 @@ data class RenderResult ( /** - * #### Generated from [EngineProcessModel.kt:75] + * #### Generated from [EngineProcessModel.kt:85] */ data class SetupContextParams ( val classpathForUrlsClassloader: List @@ -1092,7 +1092,7 @@ data class SetupContextParams ( /** - * #### Generated from [EngineProcessModel.kt:78] + * #### Generated from [EngineProcessModel.kt:88] */ data class Signature ( val name: String, @@ -1155,7 +1155,7 @@ data class Signature ( /** - * #### Generated from [EngineProcessModel.kt:26] + * #### Generated from [EngineProcessModel.kt:36] */ data class TestGeneratorParams ( val buildDir: Array, @@ -1230,7 +1230,7 @@ data class TestGeneratorParams ( /** - * #### Generated from [EngineProcessModel.kt:96] + * #### Generated from [EngineProcessModel.kt:106] */ data class WriteSarifReportArguments ( val testSetsId: Long, 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 index dc9c7ce86b..3084ac927b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessProtocolRoot.Generated.kt @@ -26,6 +26,7 @@ class EngineProcessProtocolRoot private constructor( override fun registerSerializersCore(serializers: ISerializers) { EngineProcessProtocolRoot.register(serializers) EngineProcessModel.register(serializers) + RdInstrumenterAdapter.register(serializers) RdSourceFindingStrategy.register(serializers) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt new file mode 100644 index 0000000000..005c30622c --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdInstrumenterAdapter.Generated.kt @@ -0,0 +1,159 @@ +@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 RdInstrumenterAdapter private constructor( + private val _computeSourceFileByClass: RdCall +) : RdExtBase() { + //companion + + companion object : ISerializersOwner { + + override fun registerSerializersCore(serializers: ISerializers) { + serializers.register(ComputeSourceFileByClassArguments) + } + + + @JvmStatic + @JvmName("internalCreateModel") + @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)")) + internal fun createModel(lifetime: Lifetime, protocol: IProtocol): RdInstrumenterAdapter { + @Suppress("DEPRECATION") + return create(lifetime, protocol) + } + + @JvmStatic + @Deprecated("Use protocol.rdInstrumenterAdapter or revise the extension scope instead", ReplaceWith("protocol.rdInstrumenterAdapter")) + fun create(lifetime: Lifetime, protocol: IProtocol): RdInstrumenterAdapter { + EngineProcessProtocolRoot.register(protocol.serializers) + + return RdInstrumenterAdapter().apply { + identify(protocol.identity, RdId.Null.mix("RdInstrumenterAdapter")) + bind(lifetime, protocol, "RdInstrumenterAdapter") + } + } + + private val __StringNullableSerializer = FrameworkMarshallers.String.nullable() + + const val serializationHash = -671974871925861655L + + } + override val serializersOwner: ISerializersOwner get() = RdInstrumenterAdapter + override val serializationHash: Long get() = RdInstrumenterAdapter.serializationHash + + //fields + val computeSourceFileByClass: RdCall get() = _computeSourceFileByClass + //methods + //initializer + init { + _computeSourceFileByClass.async = true + } + + init { + bindableChildren.add("computeSourceFileByClass" to _computeSourceFileByClass) + } + + //secondary constructor + private constructor( + ) : this( + RdCall(ComputeSourceFileByClassArguments, __StringNullableSerializer) + ) + + //equals trait + //hash code trait + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("RdInstrumenterAdapter (") + printer.indent { + print("computeSourceFileByClass = "); _computeSourceFileByClass.print(printer); println() + } + printer.print(")") + } + //deepClone + override fun deepClone(): RdInstrumenterAdapter { + return RdInstrumenterAdapter( + _computeSourceFileByClass.deepClonePolymorphic() + ) + } + //contexts +} +val IProtocol.rdInstrumenterAdapter get() = getOrCreateExtension(RdInstrumenterAdapter::class) { @Suppress("DEPRECATION") RdInstrumenterAdapter.create(lifetime, this) } + + + +/** + * #### Generated from [EngineProcessModel.kt:8] + */ +data class ComputeSourceFileByClassArguments ( + val className: String, + val packageName: String? +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = ComputeSourceFileByClassArguments::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): ComputeSourceFileByClassArguments { + val className = buffer.readString() + val packageName = buffer.readNullable { buffer.readString() } + return ComputeSourceFileByClassArguments(className, packageName) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: ComputeSourceFileByClassArguments) { + buffer.writeString(value.className) + buffer.writeNullable(value.packageName) { 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 ComputeSourceFileByClassArguments + + if (className != other.className) return false + if (packageName != other.packageName) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + className.hashCode() + __r = __r*31 + if (packageName != null) packageName.hashCode() else 0 + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("ComputeSourceFileByClassArguments (") + printer.indent { + print("className = "); className.print(printer); println() + print("packageName = "); packageName.print(printer); println() + } + printer.print(")") + } + //deepClone + //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 index 7e053a37f8..8fadc70d0a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/RdSourceFindingStrategy.Generated.kt @@ -15,7 +15,7 @@ import kotlin.jvm.JvmStatic /** - * #### Generated from [EngineProcessModel.kt:7] + * #### Generated from [EngineProcessModel.kt:17] */ class RdSourceFindingStrategy private constructor( private val _testsRelativePath: RdCall, @@ -111,7 +111,7 @@ val IProtocol.rdSourceFindingStrategy get() = getOrCreateExtension(RdSourceFindi /** - * #### Generated from [EngineProcessModel.kt:8] + * #### Generated from [EngineProcessModel.kt:18] */ data class SourceStrategeMethodArgs ( val classFqn: String, diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestGetSourceFileName.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestGetSourceFileName.kt index f1201f832a..866d243bc5 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestGetSourceFileName.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestGetSourceFileName.kt @@ -27,19 +27,19 @@ class TestGetSourceFileName { @Test fun testThis() { - assertEquals("TestGetSourceFileName.kt", Instrumenter.computeSourceFileName(TestGetSourceFileName::class.java)) + assertEquals("TestGetSourceFileName.kt", Instrumenter.adapter.computeSourceFileName(TestGetSourceFileName::class.java)) } @Test fun testJavaExample1() { - assertEquals("ExampleClass.java", Instrumenter.computeSourceFileName(ExampleClass::class.java)) + assertEquals("ExampleClass.java", Instrumenter.adapter.computeSourceFileName(ExampleClass::class.java)) } @Test fun testJavaExample2() { assertEquals( "ClassWithInnerClasses.java", - Instrumenter.computeSourceFileName(ClassWithInnerClasses::class.java) + Instrumenter.adapter.computeSourceFileName(ClassWithInnerClasses::class.java) ) } @@ -47,7 +47,7 @@ class TestGetSourceFileName { fun testInnerClass() { assertEquals( "ClassWithInnerClasses.java", - Instrumenter.computeSourceFileName(ClassWithInnerClasses.InnerStaticClass::class.java) + Instrumenter.adapter.computeSourceFileName(ClassWithInnerClasses.InnerStaticClass::class.java) ) } @@ -55,12 +55,12 @@ class TestGetSourceFileName { fun testSameNameButDifferentPackages() { assertEquals( true, - Instrumenter.computeSourceFileByClass(org.utbot.examples.samples.root.MyClass::class.java)?.toPath() + Instrumenter.adapter.computeSourceFileByClass(org.utbot.examples.samples.root.MyClass::class.java)?.toPath() ?.endsWith(Paths.get("root", "MyClass.java")) ) assertEquals( true, - Instrumenter.computeSourceFileByClass(org.utbot.examples.samples.root.child.MyClass::class.java) + Instrumenter.adapter.computeSourceFileByClass(org.utbot.examples.samples.root.child.MyClass::class.java) ?.toPath()?.endsWith(Paths.get("root", "child", "MyClass.java")) ) } @@ -69,7 +69,7 @@ class TestGetSourceFileName { fun testEmptyPackage() { assertEquals( true, - Instrumenter.computeSourceFileByClass(ClassWithoutPackage::class.java)?.toPath() + Instrumenter.adapter.computeSourceFileByClass(ClassWithoutPackage::class.java)?.toPath() ?.endsWith("java/ClassWithoutPackage.java") ) } @@ -78,7 +78,7 @@ class TestGetSourceFileName { fun testPackageDoesNotMatchDir() { assertEquals( true, - Instrumenter.computeSourceFileByClass(ClassWithWrongPackage::class.java)?.toPath() + Instrumenter.adapter.computeSourceFileByClass(ClassWithWrongPackage::class.java)?.toPath() ?.endsWith("org/utbot/examples/samples/ClassWithWrongPackage.kt") ) } @@ -87,7 +87,7 @@ class TestGetSourceFileName { fun testSearchDir() { assertEquals( null, - Instrumenter.computeSourceFileByClass( + Instrumenter.adapter.computeSourceFileByClass( org.utbot.examples.samples.root.MyClass::class.java, Paths.get("src/test/kotlin") )?.name @@ -95,7 +95,7 @@ class TestGetSourceFileName { assertEquals( "MyClass.java", - Instrumenter.computeSourceFileByClass( + Instrumenter.adapter.computeSourceFileByClass( org.utbot.examples.samples.root.MyClass::class.java, Paths.get("src/test") )?.name diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/Instrumenter.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/Instrumenter.kt index 70ce2a6515..dfb403e0ac 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/Instrumenter.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/Instrumenter.kt @@ -4,26 +4,11 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes -import org.objectweb.asm.Type -import org.objectweb.asm.tree.ClassNode -import org.utbot.framework.plugin.api.util.UtContext -import org.utbot.instrumentation.Settings import org.utbot.instrumentation.instrumentation.instrumenter.visitors.MethodToProbesVisitor -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.AddFieldAdapter -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.AddStaticFieldAdapter -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.IInstructionVisitor -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.InstanceFieldInitializer -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.InstructionVisitorAdapter -import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.StaticFieldInitializer +import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.* import org.utbot.instrumentation.process.HandlerClassesLoader -import java.io.File import java.io.IOException import java.io.InputStream -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.reflect.KFunction -import kotlin.reflect.jvm.javaMethod // TODO: handle with flags EXPAND_FRAMES, etc. @@ -39,7 +24,7 @@ class Instrumenter(classByteCode: ByteArray, val classLoader: ClassLoader? = nul var classByteCode: ByteArray = classByteCode.clone() private set - constructor(clazz: Class<*>) : this(computeClassBytecode(clazz)) + constructor(clazz: Class<*>) : this(adapter.computeClassBytecode(clazz)) fun visitClass(classVisitorBuilder: ClassVisitorBuilder): T { val reader = ClassReader(classByteCode) @@ -77,68 +62,7 @@ class Instrumenter(classByteCode: ByteArray, val classLoader: ClassLoader? = nul } companion object { - private fun computeClassBytecode(clazz: Class<*>): ByteArray { - val reader = - ClassReader(clazz.classLoader.getResourceAsStream(Type.getInternalName(clazz) + ".class")) - val writer = ClassWriter(reader, 0) - reader.accept(writer, 0) - return writer.toByteArray() - } - - private fun findByteClass(className: String): ClassReader? { - val path = className.replace(".", File.separator) + ".class" - return try { - val classReader = UtContext.currentContext()?.classLoader?.getResourceAsStream(path) - ?.readBytes() - ?.let { ClassReader(it) } - ?: ClassReader(className) - classReader - } catch (e: IOException) { - //TODO: SAT-1222 - null - } - } - - // TODO: move the following methods to another file - private fun computeSourceFileName(className: String): String? { - val classReader = findByteClass(className) - val sourceFileAdapter = ClassNode(Settings.ASM_API) - classReader?.accept(sourceFileAdapter, 0) - return sourceFileAdapter.sourceFile - } - - fun computeSourceFileName(clazz: Class<*>): String? { - return computeSourceFileName(clazz.name) - } - - fun computeSourceFileByMethod(method: KFunction<*>, directoryToSearchRecursively: Path = Paths.get("")): File? = - method.javaMethod?.declaringClass?.let { - computeSourceFileByClass(it, directoryToSearchRecursively) - } - - fun computeSourceFileByClass( - className: String, - packageName: String?, - directoryToSearchRecursively: Path = Paths.get("") - ): File? { - val sourceFileName = computeSourceFileName(className) ?: return null - val files = - Files.walk(directoryToSearchRecursively).filter { it.toFile().isFile && it.endsWith(sourceFileName) } - var fileWithoutPackage: File? = null - val pathWithPackage = packageName?.let { Paths.get(it, sourceFileName) } - for (f in files) { - if (pathWithPackage == null || f.endsWith(pathWithPackage)) { - return f.toFile() - } - fileWithoutPackage = f.toFile() - } - return fileWithoutPackage - } - - fun computeSourceFileByClass(clazz: Class<*>, directoryToSearchRecursively: Path = Paths.get("")): File? { - val packageName = clazz.`package`?.name?.replace('.', File.separatorChar) - return computeSourceFileByClass(clazz.name, packageName, directoryToSearchRecursively) - } + var adapter = InstrumenterAdapter() } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/InstrumenterAdapter.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/InstrumenterAdapter.kt new file mode 100644 index 0000000000..3ed3d16934 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/instrumenter/InstrumenterAdapter.kt @@ -0,0 +1,76 @@ +package org.utbot.instrumentation.instrumentation.instrumenter + +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Type +import org.objectweb.asm.tree.ClassNode +import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.instrumentation.Settings +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.reflect.KFunction +import kotlin.reflect.jvm.javaMethod + + +open class InstrumenterAdapter { + fun computeClassBytecode(clazz: Class<*>): ByteArray { + val reader = ClassReader(clazz.classLoader.getResourceAsStream(Type.getInternalName(clazz) + ".class")) + val writer = ClassWriter(reader, 0) + reader.accept(writer, 0) + return writer.toByteArray() + } + + private fun findByteClass(className: String): ClassReader? { + val path = className.replace(".", File.separator) + ".class" + return try { + val classReader = UtContext.currentContext()?.classLoader?.getResourceAsStream(path)?.readBytes() + ?.let { ClassReader(it) } ?: ClassReader(className) + classReader + } catch (e: IOException) { + //TODO: SAT-1222 + null + } + } + + // TODO: move the following methods to another file + private fun computeSourceFileName(className: String): String? { + val classReader = findByteClass(className) + val sourceFileAdapter = ClassNode(Settings.ASM_API) + classReader?.accept(sourceFileAdapter, 0) + return sourceFileAdapter.sourceFile + } + + fun computeSourceFileName(clazz: Class<*>): String? { + return computeSourceFileName(clazz.name) + } + + fun computeSourceFileByMethod(method: KFunction<*>, directoryToSearchRecursively: Path = Paths.get("")): File? = + method.javaMethod?.declaringClass?.let { + computeSourceFileByClass(it, directoryToSearchRecursively) + } + + fun computeSourceFileByClass(clazz: Class<*>, directoryToSearchRecursively: Path = Paths.get("")): File? { + val packageName = clazz.`package`?.name?.replace('.', File.separatorChar) + return computeSourceFileByClass(clazz.name, packageName, directoryToSearchRecursively) + } + + open fun computeSourceFileByClass( + className: String, packageName: String?, directoryToSearchRecursively: Path + ): File? { + val sourceFileName = computeSourceFileName(className) ?: return null + val files = + Files.walk(directoryToSearchRecursively).filter { it.toFile().isFile && it.endsWith(sourceFileName) } + var fileWithoutPackage: File? = null + val pathWithPackage = packageName?.let { Paths.get(it, sourceFileName) } + for (f in files) { + if (pathWithPackage == null || f.endsWith(pathWithPackage)) { + return f.toFile() + } + fileWithoutPackage = f.toFile() + } + return fileWithoutPackage + } +} 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 d22c335d51..db404c574f 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 @@ -613,17 +613,17 @@ object CodeGenerationController { // uploading formatted code val file = filePointer.containingFile - saveSarifReport( - proc, - testSetsId, - testClassUpdated, - classUnderTest, - model, - file?.text?: generatedTestsCode - ) - unblockDocument(testClassUpdated.project, editor.document) - - reportsCountDown.countDown() + run(THREAD_POOL) { + saveSarifReport( + proc, + testSetsId, + testClassUpdated, + classUnderTest, + model, + file?.text ?: generatedTestsCode + ) + reportsCountDown.countDown() + } } } } @@ -662,12 +662,9 @@ object CodeGenerationController { try { // saving sarif report - val sourceFinding = SourceFindingStrategyIdea(testClass) - executeCommand(testClass.project, "Saving Sarif report") { - SarifReportIdea.createAndSave(proc, testSetsId, testClassId, model, generatedTestsCode, sourceFinding) - } + SarifReportIdea.createAndSave(proc, testSetsId, testClassId, model, generatedTestsCode, testClass) } catch (e: Exception) { - logger.error { e } + logger.error(e) { "error in saving sarif report"} showErrorDialogLater( project, message = "Cannot save Sarif report via generated tests: error occurred '${e.message}'", diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index 6cf2db6779..9b8c814108 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -4,7 +4,10 @@ import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.psi.PsiMethod +import com.intellij.psi.impl.file.impl.JavaFileManager +import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.util.classMembers.MemberInfo +import com.jetbrains.rd.util.Logger import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.throwIfNotAlive import kotlinx.coroutines.runBlocking @@ -31,6 +34,7 @@ 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.loggers.UtRdKLoggerFactory import org.utbot.rd.rdPortArgument import org.utbot.rd.startUtProcessWithRdServer import org.utbot.sarif.SourceFindingStrategy @@ -49,6 +53,11 @@ private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdE data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long) class EngineProcess(parent: Lifetime, val project: Project) { + companion object { + init { + Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) + } + } private val ldef = parent.createNested() private val id = Random.nextLong() private var count = 0 @@ -137,6 +146,7 @@ class EngineProcess(parent: Lifetime, val project: Project) { } }.initModels { engineProcessModel + rdInstrumenterAdapter rdSourceFindingStrategy settingsModel.settingFor.set { params -> SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings -> @@ -175,6 +185,17 @@ class EngineProcess(parent: Lifetime, val project: Project) { isCancelled: (Unit) -> Boolean ) = runBlocking { engineModel().isCancelled.set(handler = isCancelled) + current!!.protocol.rdInstrumenterAdapter.computeSourceFileByClass.set { params -> + val result = DumbService.getInstance(project).runReadActionInSmartMode { + val scope = GlobalSearchScope.allScope(project) + val psiClass = JavaFileManager.getInstance(project) + .findClass(params.className, scope) + + psiClass?.navigationElement?.containingFile?.virtualFile?.canonicalPath + } + logger.debug("computeSourceFileByClass result: $result") + result + } engineModel().createTestGenerator.startSuspending( ldef, TestGeneratorParams(buildDir.toTypedArray(), classPath, dependencyPaths, JdkInfo(jdkInfo.path.pathString, jdkInfo.version)) @@ -286,7 +307,12 @@ class EngineProcess(parent: Lifetime, val project: Project) { testClassPackageName ) ) - result.generatedCode to kryoHelper.readObject(result.utilClassKind) + result.generatedCode to result.utilClassKind?.let { + if (UtilClassKind.RegularUtUtils.javaClass.simpleName == it) + UtilClassKind.RegularUtUtils + else + UtilClassKind.UtUtilsWithMockito + } } fun forceTermination() = runBlocking { @@ -323,7 +349,7 @@ class EngineProcess(parent: Lifetime, val project: Project) { } } } - engineModel().writeSarifReport.startSuspending(WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode)) + engineModel().writeSarifReport.start(WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode)) } fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple = runBlocking { 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 b7077863aa..b9c21a5d97 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,12 +1,13 @@ package org.utbot.intellij.plugin.sarif +import com.intellij.openapi.application.WriteAction +import com.intellij.psi.PsiClass import org.utbot.common.PathUtil.classFqnToPath -import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath -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 +import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath +import java.nio.file.Path object SarifReportIdea { @@ -20,16 +21,13 @@ object SarifReportIdea { classId: ClassId, model: GenerateTestsModel, generatedTestsCode: String, - sourceFinding: SourceFindingStrategyIdea + psiClass: PsiClass ) { // building the path to the report file val classFqn = classId.name - val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) + val (sarifReportsPath, sourceFinding) = WriteAction.computeAndWait, Exception> { model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot) to SourceFindingStrategyIdea(psiClass) } val reportFilePath = sarifReportsPath.resolve("${classFqnToPath(classFqn)}Report.sarif") - // creating report related directory - runWriteAction { VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) } - proc.writeSarif(reportFilePath, testSetsId, generatedTestsCode, sourceFinding) } } diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt index d54c9d31ee..3dc39406cf 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt @@ -4,6 +4,16 @@ import com.jetbrains.rd.generator.nova.* object EngineProcessProtocolRoot : Root() +object RdInstrumenterAdapter: Ext(EngineProcessProtocolRoot) { + val computeSourceFileByClassArguments = structdef { + field("className", PredefinedType.string) + field("packageName", PredefinedType.string.nullable) + } + init { + call("computeSourceFileByClass", computeSourceFileByClassArguments, PredefinedType.string.nullable).async + } +} + object RdSourceFindingStrategy : Ext(EngineProcessProtocolRoot) { val sourceStrategeMethodArgs = structdef { field("classFqn", PredefinedType.string) @@ -70,7 +80,7 @@ object EngineProcessModel : Ext(EngineProcessProtocolRoot) { } val renderResult = structdef { field("generatedCode", PredefinedType.string) - field("utilClassKind", array(PredefinedType.byte)) + field("utilClassKind", PredefinedType.string.nullable) } val setupContextParams = structdef { field("classpathForUrlsClassloader", immutableList(PredefinedType.string)) diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt index 7c71f4a3f4..c9656640d5 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt @@ -69,7 +69,7 @@ fun UtMethodTestSet.summarize(sourceFile: File?, searchDirectory: Path = Paths.g } fun UtMethodTestSet.summarize(searchDirectory: Path): UtMethodTestSet = - this.summarize(Instrumenter.computeSourceFileByClass(this.method.classId.jClass, searchDirectory), searchDirectory) + this.summarize(Instrumenter.adapter.computeSourceFileByClass(this.method.classId.jClass, searchDirectory), searchDirectory) class Summarization(val sourceFile: File?, val invokeDescriptions: List) { @@ -388,7 +388,7 @@ private fun invokeDescriptions(testSet: UtMethodTestSet, searchDirectory: Path): //TODO(SAT-1170) .filterNot { "\$lambda" in it.declaringClass.name } .mapNotNull { sootMethod -> - val methodFile = Instrumenter.computeSourceFileByClass( + val methodFile = Instrumenter.adapter.computeSourceFileByClass( sootMethod.declaringClass.name, sootMethod.declaringClass.javaPackageName.replace(".", File.separator), searchDirectory