Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Frosendroska committed Jul 29, 2024
1 parent fa17914 commit f77a23f
Show file tree
Hide file tree
Showing 26 changed files with 2,380 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.jetbrains.research.testspark.core.test

/**
* Language ID string should be the same as the language name in com.intellij.lang.Language
*/
enum class SupportedLanguage(val languageId: String) {
Java("JAVA"), Kotlin("kotlin")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.jetbrains.research.testspark.core.test

import org.jetbrains.research.testspark.core.test.data.TestLine

interface TestBodyPrinter {
/**
* Generates a test body as a string based on the provided parameters.
*
* @param testInitiatedText A string containing the upper part of the test case.
* @param lines A mutable list of `TestLine` objects representing the lines of the test body.
* @param throwsException The exception type that the test function throws, if any.
* @param name The name of the test function.
* @return A string representing the complete test body.
*/
fun printTestBody(
testInitiatedText: String,
lines: MutableList<TestLine>,
throwsException: String,
name: String,
): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jetbrains.research.testspark.core.test

import org.jetbrains.research.testspark.core.test.data.TestCaseGeneratedByLLM
import org.jetbrains.research.testspark.core.test.data.TestSuiteGeneratedByLLM

data class TestCaseParseResult(
val testCase: TestCaseGeneratedByLLM?,
val errorMessage: String,
val errorOccurred: Boolean,
)

interface TestSuiteParser {
/**
* Extracts test cases from raw text and generates a test suite.
*
* @param rawText The raw text provided by the LLM that contains the generated test cases.
* @return A GeneratedTestSuite instance containing the extracted test cases.
*/
fun parseTestSuite(rawText: String): TestSuiteGeneratedByLLM?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.jetbrains.research.testspark.core.test.data

/**
* Enum class, which contains all code elements for which it is possible to request test generation.
*/
enum class CodeType {
CLASS, METHOD, LINE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.jetbrains.research.testspark.core.test.data.dependencies

import org.jetbrains.research.testspark.core.data.JarLibraryDescriptor

/**
* The class represents a list of dependencies required for java test compilation.
* The libraries listed are used during test suite/test case compilation.
*/
class TestCompilationDependencies {
companion object {
fun getJarDescriptors() = listOf(
JarLibraryDescriptor(
"mockito-core-5.0.0.jar",
"https://repo1.maven.org/maven2/org/mockito/mockito-core/5.0.0/mockito-core-5.0.0.jar",
),
JarLibraryDescriptor(
"hamcrest-core-1.3.jar",
"https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
),
JarLibraryDescriptor(
"byte-buddy-1.14.6.jar",
"https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy/1.14.6/byte-buddy-1.14.6.jar",
),
JarLibraryDescriptor(
"byte-buddy-agent-1.14.6.jar",
"https://repo1.maven.org/maven2/net/bytebuddy/byte-buddy-agent/1.14.6/byte-buddy-agent-1.14.6.jar",
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jetbrains.research.testspark.core.test.java

import org.jetbrains.research.testspark.core.data.JUnitVersion
import org.jetbrains.research.testspark.core.generation.llm.getPackageFromTestSuiteCode
import org.jetbrains.research.testspark.core.test.SupportedLanguage
import org.jetbrains.research.testspark.core.test.TestBodyPrinter
import org.jetbrains.research.testspark.core.test.TestSuiteParser
import org.jetbrains.research.testspark.core.test.data.TestSuiteGeneratedByLLM
import org.jetbrains.research.testspark.core.test.strategies.JUnitTestSuiteParserStrategy
import org.jetbrains.research.testspark.core.utils.javaImportPattern

class JavaJUnitTestSuiteParser(
private var packageName: String,
private val junitVersion: JUnitVersion,
private val testBodyPrinter: TestBodyPrinter,
) : TestSuiteParser {
override fun parseTestSuite(rawText: String): TestSuiteGeneratedByLLM? {
val packageInsideTestText = getPackageFromTestSuiteCode(rawText, SupportedLanguage.Java)
if (packageInsideTestText.isNotBlank()) {
packageName = packageInsideTestText
}

return JUnitTestSuiteParserStrategy.parseJUnitTestSuite(
rawText,
junitVersion,
javaImportPattern,
packageName,
testNamePattern = "void",
testBodyPrinter,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jetbrains.research.testspark.core.test.java

import org.jetbrains.research.testspark.core.test.TestBodyPrinter
import org.jetbrains.research.testspark.core.test.data.TestLine
import org.jetbrains.research.testspark.core.test.data.TestLineType

class JavaTestBodyPrinter : TestBodyPrinter {
override fun printTestBody(
testInitiatedText: String,
lines: MutableList<TestLine>,
throwsException: String,
name: String,
): String {
var testFullText = testInitiatedText

// start writing the test signature
testFullText += "\n\tpublic void $name() "

// add throws exception if exists
if (throwsException.isNotBlank()) {
testFullText += "throws $throwsException"
}

// start writing the test lines
testFullText += "{\n"

// write each line
lines.forEach { line ->
testFullText += when (line.type) {
TestLineType.BREAK -> "\t\t\n"
else -> "\t\t${line.text}\n"
}
}

// close test case
testFullText += "\t}\n"

return testFullText
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.jetbrains.research.testspark.core.test.java

import io.github.oshai.kotlinlogging.KotlinLogging
import org.jetbrains.research.testspark.core.test.TestCompiler
import org.jetbrains.research.testspark.core.utils.CommandLineRunner
import org.jetbrains.research.testspark.core.utils.DataFilesUtil
import java.io.File

class JavaTestCompiler(
libPaths: List<String>,
junitLibPaths: List<String>,
private val javaHomeDirectoryPath: String,
) : TestCompiler(libPaths, junitLibPaths) {

private val log = KotlinLogging.logger { this::class.java }

override fun compileCode(path: String, projectBuildPath: String): Pair<Boolean, String> {
val classPaths = "\"${getClassPaths(projectBuildPath)}\""
// find the proper javac
val javaCompile = File(javaHomeDirectoryPath).walk()
.filter {
val isCompilerName =
if (DataFilesUtil.isWindows()) it.name.equals("javac.exe") else it.name.equals("javac")
isCompilerName && it.isFile
}
.firstOrNull()

if (javaCompile == null) {
val msg = "Cannot find java compiler 'javac' at '$javaHomeDirectoryPath'"
log.error { msg }
throw RuntimeException(msg)
}

println("javac found at '${javaCompile.absolutePath}'")

// compile file
val errorMsg = CommandLineRunner.run(
arrayListOf(
javaCompile.absolutePath,
"-cp",
classPaths,
path,
),
)

log.info { "Error message: '$errorMsg'" }
// create .class file path
val classFilePath = path.replace(".java", ".class")

// check is .class file exists
return Pair(File(classFilePath).exists(), errorMsg)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jetbrains.research.testspark.core.test.kotlin

import org.jetbrains.research.testspark.core.data.JUnitVersion
import org.jetbrains.research.testspark.core.generation.llm.getPackageFromTestSuiteCode
import org.jetbrains.research.testspark.core.test.SupportedLanguage
import org.jetbrains.research.testspark.core.test.TestBodyPrinter
import org.jetbrains.research.testspark.core.test.TestSuiteParser
import org.jetbrains.research.testspark.core.test.data.TestSuiteGeneratedByLLM
import org.jetbrains.research.testspark.core.test.strategies.JUnitTestSuiteParserStrategy
import org.jetbrains.research.testspark.core.utils.kotlinImportPattern

class KotlinJUnitTestSuiteParser(
private var packageName: String,
private val junitVersion: JUnitVersion,
private val testBodyPrinter: TestBodyPrinter,
) : TestSuiteParser {
override fun parseTestSuite(rawText: String): TestSuiteGeneratedByLLM? {
val packageInsideTestText = getPackageFromTestSuiteCode(rawText, SupportedLanguage.Kotlin)
if (packageInsideTestText.isNotBlank()) {
packageName = packageInsideTestText
}

return JUnitTestSuiteParserStrategy.parseJUnitTestSuite(
rawText,
junitVersion,
kotlinImportPattern,
packageName,
testNamePattern = "fun",
testBodyPrinter,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jetbrains.research.testspark.core.test.kotlin

import org.jetbrains.research.testspark.core.test.TestBodyPrinter
import org.jetbrains.research.testspark.core.test.data.TestLine
import org.jetbrains.research.testspark.core.test.data.TestLineType

class KotlinTestBodyPrinter : TestBodyPrinter {
override fun printTestBody(
testInitiatedText: String,
lines: MutableList<TestLine>,
throwsException: String,
name: String,
): String {
var testFullText = testInitiatedText

// start writing the test signature
testFullText += "\n\tfun $name() "

// add throws exception if exists
if (throwsException.isNotBlank()) {
testFullText += "throws $throwsException"
}

// start writing the test lines
testFullText += "{\n"

// write each line
lines.forEach { line ->
testFullText += when (line.type) {
TestLineType.BREAK -> "\t\t\n"
else -> "\t\t${line.text}\n"
}
}

// close test case
testFullText += "\t}\n"

return testFullText
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.jetbrains.research.testspark.core.test.kotlin

import io.github.oshai.kotlinlogging.KotlinLogging
import org.jetbrains.research.testspark.core.test.TestCompiler
import org.jetbrains.research.testspark.core.utils.CommandLineRunner

class KotlinTestCompiler(libPaths: List<String>, junitLibPaths: List<String>) :
TestCompiler(libPaths, junitLibPaths) {

private val log = KotlinLogging.logger { this::class.java }

override fun compileCode(path: String, projectBuildPath: String): Pair<Boolean, String> {
log.info { "[KotlinTestCompiler] Compiling ${path.substringAfterLast('/')}" }

val classPaths = "\"${getClassPaths(projectBuildPath)}\""
// Compile file
val errorMsg = CommandLineRunner.run(
arrayListOf(
"kotlinc",
"-cp",
classPaths,
path,
),
)

log.info { "Error message: '$errorMsg'" }

// No need to save the .class file for kotlin, so checking the error message is enough
return Pair(errorMsg.isBlank(), errorMsg)
}
}
Loading

0 comments on commit f77a23f

Please sign in to comment.