Skip to content

Commit

Permalink
Merge pull request #56 from JetBrains-Research/test-execution
Browse files Browse the repository at this point in the history
Highlighting
  • Loading branch information
arksap2002 authored Aug 24, 2023
2 parents 460da6d + 6db3c0b commit 04767c3
Show file tree
Hide file tree
Showing 26 changed files with 972 additions and 668 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ dependencies {
implementation(files("lib/jacocoagent.jar"))
implementation(files("lib/jacococli.jar"))
implementation(files("lib/mockito-core-5.0.0.jar"))
implementation(files("lib/byte-buddy-1.14.6.jar"))
implementation(files("lib/byte-buddy-agent-1.14.6.jar"))
implementation(files("lib/JUnitRunner.jar"))

// validation dependencies
Expand Down
Binary file added lib/byte-buddy-1.14.6.jar
Binary file not shown.
Binary file added lib/byte-buddy-agent-1.14.6.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class GenerateTestsActionClassLlm : AnAction() {
*
* @param e an action event that contains useful information and corresponds to the action invoked by the user
*/
override fun update(e: AnActionEvent) = updateForClass(e, "GPT-4")
override fun update(e: AnActionEvent) = updateForClass(e, "LLM")

/**
* AnAction.getActionUpdateThread() implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class GenerateTestsActionLineLlm : AnAction() {
*
* @param e an action event that contains useful information and corresponds to the action invoked by the user
*/
override fun update(e: AnActionEvent) = updateForLine(e, "GPT-4")
override fun update(e: AnActionEvent) = updateForLine(e, "LLM")

/**
* AnAction.getActionUpdateThread() implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class GenerateTestsActionMethodLlm : AnAction() {
*
* @param e an action event that contains useful information and corresponds to the action invoked by the user
*/
override fun update(e: AnActionEvent) = updateForMethod(e, "GPT-4")
override fun update(e: AnActionEvent) = updateForMethod(e, "LLM")

/**
* AnAction.getActionUpdateThread() implementation.
Expand Down
106 changes: 82 additions & 24 deletions src/main/kotlin/org/jetbrains/research/testspark/editor/Workspace.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiClass
import org.jetbrains.research.testspark.data.Report
import org.jetbrains.research.testspark.data.TestCase
import org.jetbrains.research.testspark.data.TestGenerationData
import org.jetbrains.research.testspark.services.COVERAGE_SELECTION_TOGGLE_TOPIC
import org.jetbrains.research.testspark.services.CoverageSelectionToggleListener
import org.jetbrains.research.testspark.services.CoverageVisualisationService
import org.jetbrains.research.testspark.services.ErrorService
import org.jetbrains.research.testspark.services.TestCaseDisplayService
import org.jetbrains.research.testspark.services.*
import org.jetbrains.research.testspark.tools.evosuite.validation.VALIDATION_RESULT_TOPIC
import org.jetbrains.research.testspark.tools.evosuite.validation.ValidationResultListener
import org.jetbrains.research.testspark.tools.evosuite.validation.Validator
import java.io.File

/**
* Workspace state service
Expand All @@ -45,7 +44,7 @@ class Workspace(private val project: Project) : Disposable {

class TestJob(
val info: TestJobInfo,
val report: Report,
var report: Report,
val selectedTests: HashSet<String>,
) {
private fun getSelectedTests(): List<TestCase> {
Expand All @@ -57,8 +56,40 @@ class Workspace(private val project: Project) : Disposable {
getSelectedTests().map { lineSet.addAll(it.coveredLines) }
return lineSet
}

fun updateReport(report: Report) {
this.report = report
}
}

var editor: Editor? = null
var testJob: TestJob? = null

// The class path of the project.
var projectClassPath: String? = null
var testResultDirectory: String? = null
var testResultName: String? = null

// The path to save the generated test results.
var resultPath: String? = null

// The base directory of the project.
var baseDir: String? = null
var vFile: VirtualFile? = null

// The URL of the file being tested.
var fileUrl: String? = null

// The modification stamp of the file being tested.
var modificationStamp: Long? = null
var cutPsiClass: PsiClass? = null

// The module to cut.
var cutModule: Module? = null

// The fully qualified name of the class being tested.
var classFQN: String? = null

private val log = Logger.getInstance(this.javaClass)

private var listenerDisposable: Disposable? = null
Expand Down Expand Up @@ -141,6 +172,8 @@ class Workspace(private val project: Project) : Disposable {
project.service<ErrorService>().clear()
project.service<CoverageVisualisationService>().clear()
testGenerationData.clear()
project.service<Workspace>().cleanFolder(resultPath!!)
project.service<TestsExecutionResultService>().clear()
}

/**
Expand All @@ -165,13 +198,15 @@ class Workspace(private val project: Project) : Disposable {
val displayedSet = HashSet<String>()
displayedSet.addAll(testReport.testCaseList.keys)

val testJob = TestJob(jobKey, testReport, displayedSet)
resultsForFile.add(testJob)
testJob = TestJob(jobKey, testReport, displayedSet)
resultsForFile.add(testJob!!)

val editor = editorForFileUrl(jobKey.fileUrl)
updateEditorForFileUrl(jobKey.fileUrl)

project.service<Workspace>().cleanFolder(resultPath!!)

if (editor != null) {
showReport(testJob, editor)
showReport()
} else {
log.info("No editor opened for received test result")
}
Expand All @@ -183,18 +218,17 @@ class Workspace(private val project: Project) : Disposable {
* Utility function that returns the editor for a specific file url,
* in case it is opened in the IDE
*/
fun editorForFileUrl(fileUrl: String): Editor? {
private fun updateEditorForFileUrl(fileUrl: String) {
val documentManager = FileDocumentManager.getInstance()
// https://intellij-support.jetbrains.com/hc/en-us/community/posts/360004480599/comments/360000703299
FileEditorManager.getInstance(project).selectedEditors.map { it as TextEditor }.map { it.editor }.map {
val currentFile = documentManager.getFile(it.document)
if (currentFile != null) {
if (currentFile.presentableUrl == fileUrl) {
return it
editor = it
}
}
}
return null
}

/**
Expand Down Expand Up @@ -223,22 +257,23 @@ class Workspace(private val project: Project) : Disposable {
return documentManager.getFile(document)
}

fun updateTestCase(testCase: TestCase) {
val updatedReport = testJob!!.report
updatedReport.testCaseList.remove(testCase.testName)
updatedReport.testCaseList[testCase.testName] = testCase
updatedReport.normalized()
testJob!!.updateReport(updatedReport)
project.service<CoverageVisualisationService>().showCoverage(updatedReport, editor!!)
}

/**
* Function that calls the services responsible for visualizing
* coverage and displaying the generated test cases. This
* is used whenever a new test generation result gets published.
*
* @param testJob the new test job
* @param editor editor instance where coverage should be
* visualized
* @param cacheLazyPipeline the runner that was instantiated but not used to create the test suite
* due to a cache hit, or null if there was a cache miss
*/
private fun showReport(testJob: TestJob, editor: Editor) {
val visualizationService = project.service<CoverageVisualisationService>()
val testCaseDisplayService = project.service<TestCaseDisplayService>()
testCaseDisplayService.showGeneratedTests(testJob, editor)
visualizationService.showCoverage(testJob.report, editor)
private fun showReport() {
project.service<TestCaseDisplayService>().showGeneratedTests(editor!!)
project.service<CoverageVisualisationService>().showCoverage(testJob!!.report, editor!!)
}

/**
Expand Down Expand Up @@ -282,4 +317,27 @@ class Workspace(private val project: Project) : Disposable {
override fun dispose() {
listenerDisposable?.let { Disposer.dispose(it) }
}

/**
* Clean data folder
*/
fun cleanFolder(path: String) {
val folder = File(path)

if (!folder.exists()) return

if (folder.isDirectory) {
val files = folder.listFiles()
if (files != null) {
for (file in files) {
if (file.isDirectory) {
cleanFolder(file.absolutePath)
} else {
file.delete()
}
}
}
}
folder.delete()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.jetbrains.research.testspark.services

import com.intellij.openapi.project.Project

class JavaClassBuilderService(private val project: Project) {
/**
* Generates the code for a test class.
*
* @param className the name of the test class
* @param body the body of the test class
* @return the generated code as a string
*/
fun generateCode(
className: String,
body: String,
imports: Set<String>,
packageString: String,
runWith: String,
otherInfo: String,
): String {
var testFullText = printUpperPart(className, imports, packageString, runWith, otherInfo)

// Add each test (exclude expected exception)
testFullText += body

// close the test class
testFullText += "}"

return testFullText
}

/**
* Returns the upper part of test suite (package name, imports, and test class name) as a string.
*
* @return the upper part of test suite (package name, imports, and test class name) as a string.
*/
private fun printUpperPart(
className: String,
imports: Set<String>,
packageString: String,
runWith: String,
otherInfo: String,
): String {
var testText = ""

// Add package
if (packageString.isNotBlank()) {
testText += "package $packageString;\n"
}

// add imports
imports.forEach { importedElement ->
testText += "$importedElement\n"
}

testText += "\n"

// add runWith if exists
if (runWith.isNotBlank()) {
testText += "@RunWith($runWith)\n"
}
// open the test class
testText += "public class $className{\n\n"

// Add other presets (annotations, non-test functions)
if (otherInfo.isNotBlank()) {
testText += otherInfo
}

return testText
}
}
Loading

0 comments on commit 04767c3

Please sign in to comment.