diff --git a/.gitignore b/.gitignore index aed022b20..8291da049 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ lib/evosuite-*.jar lib/JUnitRunner.jar **/evosuite-tests/ *.DS_Store +.intellijPlatform/ # Test projects src/test/resources/project/.idea/ diff --git a/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkAction.kt b/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkAction.kt index 5f537ac89..e519ef8bd 100644 --- a/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkAction.kt +++ b/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkAction.kt @@ -1,53 +1,14 @@ package org.jetbrains.research.testspark.actions -import com.intellij.notification.NotificationGroupManager -import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.project.Project -import com.intellij.ui.components.JBScrollPane -import com.intellij.util.ui.FormBuilder import org.jetbrains.research.testspark.actions.controllers.TestGenerationController import org.jetbrains.research.testspark.actions.controllers.VisibilityController -import org.jetbrains.research.testspark.actions.evosuite.EvoSuitePanelBuilder -import org.jetbrains.research.testspark.actions.llm.LLMSampleSelectorBuilder -import org.jetbrains.research.testspark.actions.llm.LLMSetupPanelBuilder -import org.jetbrains.research.testspark.actions.template.PanelBuilder -import org.jetbrains.research.testspark.bundles.plugin.PluginLabelsBundle -import org.jetbrains.research.testspark.bundles.plugin.PluginMessagesBundle -import org.jetbrains.research.testspark.core.test.data.CodeType import org.jetbrains.research.testspark.display.TestSparkDisplayManager -import org.jetbrains.research.testspark.display.TestSparkIcons -import org.jetbrains.research.testspark.langwrappers.PsiHelper import org.jetbrains.research.testspark.langwrappers.PsiHelperProvider -import org.jetbrains.research.testspark.services.EvoSuiteSettingsService -import org.jetbrains.research.testspark.services.LLMSettingsService -import org.jetbrains.research.testspark.settings.evosuite.EvoSuiteSettingsState -import org.jetbrains.research.testspark.settings.llm.LLMSettingsState import org.jetbrains.research.testspark.tools.TestsExecutionResultManager -import org.jetbrains.research.testspark.tools.evosuite.EvoSuite -import org.jetbrains.research.testspark.tools.kex.Kex -import org.jetbrains.research.testspark.tools.llm.Llm -import org.jetbrains.research.testspark.tools.template.Tool -import java.awt.BorderLayout -import java.awt.CardLayout -import java.awt.Component -import java.awt.Dimension -import java.awt.Font -import java.awt.Toolkit -import java.awt.event.WindowAdapter -import java.awt.event.WindowEvent -import javax.swing.Box -import javax.swing.BoxLayout -import javax.swing.ButtonGroup -import javax.swing.JButton -import javax.swing.JFrame -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JRadioButton -import javax.swing.SwingConstants /** * Represents an action to be performed in the TestSpark plugin. @@ -56,12 +17,6 @@ import javax.swing.SwingConstants * It creates a dialog wrapper and displays it when the associated action is performed. */ class TestSparkAction : AnAction() { - // Controllers - private val visibilityController = VisibilityController() - private val testGenerationController = TestGenerationController() - - private val testSparkDisplayManager = TestSparkDisplayManager() - private val testsExecutionResultManager = TestsExecutionResultManager() /** * Handles the action performed event. @@ -73,7 +28,13 @@ class TestSparkAction : AnAction() { * This parameter is required. */ override fun actionPerformed(e: AnActionEvent) { - TestSparkActionWindow(e, visibilityController, testGenerationController, testSparkDisplayManager, testsExecutionResultManager) + TestSparkActionWindow( + e = e, + visibilityController = VisibilityController(), + testGenerationController = TestGenerationController(), + testSparkDisplayManager = TestSparkDisplayManager(), + testsExecutionResultManager = TestsExecutionResultManager(), + ) } /** @@ -93,360 +54,5 @@ class TestSparkAction : AnAction() { e.presentation.isEnabledAndVisible = (psiHelper != null) && psiHelper.availableForGeneration(e) } - /** - * Class representing the TestSparkActionWindow. - * - * @property e The AnActionEvent object. - */ - class TestSparkActionWindow( - private val e: AnActionEvent, - private val visibilityController: VisibilityController, - private val testGenerationController: TestGenerationController, - private val testSparkDisplayManager: TestSparkDisplayManager, - private val testsExecutionResultManager: TestsExecutionResultManager, - ) : - JFrame("TestSpark") { - private val project: Project = e.project!! - - private val llmSettingsState: LLMSettingsState - get() = project.getService(LLMSettingsService::class.java).state - private val evoSuiteSettingsState: EvoSuiteSettingsState - get() = project.getService(EvoSuiteSettingsService::class.java).state - - private val llmButton = JRadioButton("${Llm().name}") - private val evoSuiteButton = JRadioButton("${EvoSuite().name}") - private val kexButton = JRadioButton("${Kex().name}") - private val testGeneratorButtonGroup = ButtonGroup() - private val kexForLineCodeTypeErrMsg = JLabel() // The error displayed when if kex and line code type are chosen - - private val psiHelper: PsiHelper - get() { - val file = e.dataContext.getData(CommonDataKeys.PSI_FILE)!! - val psiHelper = PsiHelperProvider.getPsiHelper(file) - if (psiHelper == null) { - // TODO exception - } - return psiHelper!! - } - - private val codeTypes = psiHelper.getCurrentListOfCodeTypes(e) - private val caretOffset: Int = e.dataContext.getData(CommonDataKeys.CARET)?.caretModel?.primaryCaret!!.offset - private val fileUrl = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE)!!.presentableUrl - - private val codeTypeButtons: MutableList> = mutableListOf() - private val codeTypeButtonGroup = ButtonGroup() - - private val nextButton = JButton(PluginLabelsBundle.get("next")) - - private val cardLayout = CardLayout() - private val llmSetupPanelFactory = LLMSetupPanelBuilder(e, project) - private val llmSampleSelectorFactory = LLMSampleSelectorBuilder(project, psiHelper.language) - private val evoSuitePanelFactory = EvoSuitePanelBuilder(project) - - init { - if (!visibilityController.isVisible) { - visibilityController.isVisible = true - isVisible = true - - val panel = JPanel(cardLayout) - - panel.add(getMainPanel(), "1") - panel.add(createCardPanel(evoSuitePanelFactory), "2") - panel.add(createCardPanel(llmSetupPanelFactory), "3") - - panel.add( - JBScrollPane( - createCardPanel(llmSampleSelectorFactory), - JBScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER, - ), - "4", - ) - - addListeners(panel) - - add(panel) - - pack() - - val dimension: Dimension = Toolkit.getDefaultToolkit().screenSize - val x = (dimension.width - size.width) / 2 - val y = (dimension.height - size.height) / 2 - setLocation(x, y) - } else { - NotificationGroupManager.getInstance() - .getNotificationGroup("Generation Error") - .createNotification( - PluginMessagesBundle.get("generationWindowWarningTitle"), - PluginMessagesBundle.get("generationWindowWarningMessage"), - NotificationType.WARNING, - ) - .notify(e.project) - } - } - - private fun createCardPanel(toolPanelBuilder: PanelBuilder): JPanel { - val cardPanel = JPanel(BorderLayout()) - cardPanel.add(toolPanelBuilder.getTitlePanel(), BorderLayout.NORTH) - cardPanel.add(toolPanelBuilder.getMiddlePanel(), BorderLayout.CENTER) - cardPanel.add(toolPanelBuilder.getBottomPanel(), BorderLayout.SOUTH) - - return cardPanel - } - - /** - * Returns the main panel for the test generator UI. - * This panel contains options for selecting the test generator and the code type. - * It also includes a button for proceeding to the next step. - * - * @return the main panel for the test generator UI - */ - private fun getMainPanel(): JPanel { - val panelTitle = JPanel() - val textTitle = JLabel("Welcome to TestSpark!") - textTitle.font = Font("Monochrome", Font.BOLD, 20) - panelTitle.add(JLabel(TestSparkIcons.pluginIcon)) - panelTitle.add(textTitle) - - if (Llm().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(llmButton) - if (EvoSuite().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(evoSuiteButton) - if (Kex().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(kexButton) - - val testGeneratorPanel = JPanel() - testGeneratorPanel.add(JLabel("Select the test generator:")) - for (button in testGeneratorButtonGroup.elements) testGeneratorPanel.add(button) - if (testGeneratorButtonGroup.elements.toList().size == 1) { - // A single button is selected by default - testGeneratorButtonGroup.elements.toList()[0].isSelected = true - } - - for ((codeType, codeTypeName) in codeTypes) { - val button = JRadioButton(codeTypeName) - codeTypeButtons.add(codeType to button) - codeTypeButtonGroup.add(button) - } - - val codesToTestPanel = JPanel() - codesToTestPanel.add(JLabel("Select the code type:")) - if (codeTypeButtons.size == 1) { - // A single button is selected by default - codeTypeButtons[0].second.isSelected = true - } - for ((_, button) in codeTypeButtons) codesToTestPanel.add(button) - - val middlePanel = FormBuilder.createFormBuilder() - .setFormLeftIndent(10) - .addComponent( - testGeneratorPanel, - 10, - ) - .addComponent( - codesToTestPanel, - 10, - ) - .panel - - val nextButtonPanel = JPanel() - nextButtonPanel.layout = BoxLayout(nextButtonPanel, BoxLayout.Y_AXIS) - nextButton.isEnabled = false - nextButton.alignmentX = Component.CENTER_ALIGNMENT - kexForLineCodeTypeErrMsg.alignmentX = Component.CENTER_ALIGNMENT - kexForLineCodeTypeErrMsg.horizontalAlignment = SwingConstants.CENTER - nextButtonPanel.add(kexForLineCodeTypeErrMsg) - nextButtonPanel.add(Box.createVerticalStrut(10)) // Add some space between label and button - nextButtonPanel.add(nextButton) - updateNextButton() - - val cardPanel = JPanel(BorderLayout()) - cardPanel.add(panelTitle, BorderLayout.NORTH) - cardPanel.add(middlePanel, BorderLayout.CENTER) - cardPanel.add(nextButtonPanel, BorderLayout.SOUTH) - - return cardPanel - } - - /** - * Adds listeners to various components in the given panel. - * - * @param panel the JPanel to add listeners to - */ - private fun addListeners(panel: JPanel) { - addWindowListener(object : WindowAdapter() { - override fun windowClosing(e: WindowEvent?) { - visibilityController.isVisible = false - } - }) - - llmButton.addActionListener { - updateNextButton() - } - - evoSuiteButton.addActionListener { - updateNextButton() - } - - kexButton.addActionListener { - updateNextButton() - } - - for ((_, button) in codeTypeButtons) { - button.addActionListener { - llmSetupPanelFactory.setPromptEditorType(button.text) - updateNextButton() - } - } - - nextButton.addActionListener { - if (llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected && !llmSettingsState.provideTestSamplesCheckBoxSelected) { - startLLMGeneration() - } else if (llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected) { - cardLayout.next(panel) - cardLayout.next(panel) - cardLayout.next(panel) - pack() - } else if (llmButton.isSelected) { - cardLayout.next(panel) - cardLayout.next(panel) - pack() - } else if (kexButton.isSelected) { - startKexGeneration() - } else if (evoSuiteButton.isSelected && !evoSuiteSettingsState.evosuiteSetupCheckBoxSelected) { - startEvoSuiteGeneration() - } else { - cardLayout.next(panel) - pack() - } - } - - evoSuitePanelFactory.getBackButton().addActionListener { - cardLayout.previous(panel) - pack() - } - - llmSetupPanelFactory.getBackButton().addActionListener { - cardLayout.previous(panel) - cardLayout.previous(panel) - pack() - } - - llmSetupPanelFactory.getFinishedButton().addActionListener { - llmSetupPanelFactory.applyUpdates() - if (llmSettingsState.provideTestSamplesCheckBoxSelected) { - cardLayout.next(panel) - } else { - startLLMGeneration() - } - } - - llmSampleSelectorFactory.getAddButton().addActionListener { - size = Dimension(width, 500) - } - - llmSampleSelectorFactory.getBackButton().addActionListener { - if (llmSettingsState.llmSetupCheckBoxSelected) { - cardLayout.previous(panel) - } else { - cardLayout.previous(panel) - cardLayout.previous(panel) - cardLayout.previous(panel) - } - pack() - } - - llmSampleSelectorFactory.getFinishedButton().addActionListener { - llmSampleSelectorFactory.applyUpdates() - startLLMGeneration() - } - - evoSuitePanelFactory.getFinishedButton().addActionListener { - evoSuitePanelFactory.applyUpdates() - startEvoSuiteGeneration() - } - } - - private fun startUnitTestGenerationTool(tool: Tool) { - if (!testGenerationController.isGeneratorRunning(project)) { - val testSamplesCode = llmSampleSelectorFactory.getTestSamplesCode() - - for ((codeType, button) in codeTypeButtons) { - if (button.isSelected) { - when (codeType) { - CodeType.CLASS -> tool.generateTestsForClass( - project, - psiHelper, - caretOffset, - fileUrl, - testSamplesCode, - testGenerationController, - testSparkDisplayManager, - testsExecutionResultManager, - ) - CodeType.METHOD -> tool.generateTestsForMethod( - project, - psiHelper, - caretOffset, - fileUrl, - testSamplesCode, - testGenerationController, - testSparkDisplayManager, - testsExecutionResultManager, - ) - CodeType.LINE -> tool.generateTestsForLine( - project, - psiHelper, - caretOffset, - fileUrl, - testSamplesCode, - testGenerationController, - testSparkDisplayManager, - testsExecutionResultManager, - ) - } - break - } - } - } - - visibilityController.isVisible = false - dispose() - } - - private fun startKexGeneration() = startUnitTestGenerationTool(tool = Kex()) - private fun startEvoSuiteGeneration() = startUnitTestGenerationTool(tool = EvoSuite()) - private fun startLLMGeneration() = startUnitTestGenerationTool(tool = Llm()) - - /** - * Updates the state of the "Next" button based on the selected options. - * The "Next" button is enabled only if a test generator button (llmButton or evoSuiteButton) and at least one - * code type button (from codeTypeButtons) are selected. - * - * This method should be called whenever the mentioned above buttons are clicked. - */ - private fun updateNextButton() { - val isTestGeneratorButtonGroupSelected = llmButton.isSelected || evoSuiteButton.isSelected || kexButton.isSelected - val isCodeTypeButtonGroupSelected = codeTypeButtons.any { it.second.isSelected } - val kexForCodeLineType = - kexButton.isSelected && codeTypeButtons.any { (codeType, button) -> codeType == CodeType.LINE && button.isSelected } - if (kexForCodeLineType) { - kexForLineCodeTypeErrMsg.text = - "* Kex cannot generate tests for a single line. Please change your selection" - } else { - kexForLineCodeTypeErrMsg.text = "" - } - - nextButton.isEnabled = - isTestGeneratorButtonGroupSelected && isCodeTypeButtonGroupSelected && !kexForCodeLineType - - if ((llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected && !llmSettingsState.provideTestSamplesCheckBoxSelected) || - (evoSuiteButton.isSelected && !evoSuiteSettingsState.evosuiteSetupCheckBoxSelected) || - kexButton.isSelected - ) { - nextButton.text = PluginLabelsBundle.get("ok") - } else { - nextButton.text = PluginLabelsBundle.get("next") - } - } - } - override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT } diff --git a/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkActionWindow.kt b/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkActionWindow.kt new file mode 100644 index 000000000..a8ca7ac08 --- /dev/null +++ b/src/main/kotlin/org/jetbrains/research/testspark/actions/TestSparkActionWindow.kt @@ -0,0 +1,402 @@ +package org.jetbrains.research.testspark.actions + +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.project.Project +import com.intellij.ui.components.JBScrollPane +import com.intellij.util.ui.FormBuilder +import org.jetbrains.research.testspark.actions.controllers.TestGenerationController +import org.jetbrains.research.testspark.actions.controllers.VisibilityController +import org.jetbrains.research.testspark.actions.evosuite.EvoSuitePanelBuilder +import org.jetbrains.research.testspark.actions.llm.LLMSampleSelectorBuilder +import org.jetbrains.research.testspark.actions.llm.LLMSetupPanelBuilder +import org.jetbrains.research.testspark.actions.template.PanelBuilder +import org.jetbrains.research.testspark.bundles.plugin.PluginLabelsBundle +import org.jetbrains.research.testspark.bundles.plugin.PluginMessagesBundle +import org.jetbrains.research.testspark.core.test.data.CodeType +import org.jetbrains.research.testspark.display.TestSparkDisplayManager +import org.jetbrains.research.testspark.display.TestSparkIcons +import org.jetbrains.research.testspark.langwrappers.PsiHelper +import org.jetbrains.research.testspark.langwrappers.PsiHelperProvider +import org.jetbrains.research.testspark.services.EvoSuiteSettingsService +import org.jetbrains.research.testspark.services.LLMSettingsService +import org.jetbrains.research.testspark.settings.evosuite.EvoSuiteSettingsState +import org.jetbrains.research.testspark.settings.llm.LLMSettingsState +import org.jetbrains.research.testspark.tools.TestsExecutionResultManager +import org.jetbrains.research.testspark.tools.evosuite.EvoSuite +import org.jetbrains.research.testspark.tools.kex.Kex +import org.jetbrains.research.testspark.tools.llm.Llm +import org.jetbrains.research.testspark.tools.template.Tool +import java.awt.BorderLayout +import java.awt.CardLayout +import java.awt.Dimension +import java.awt.Font +import java.awt.Toolkit +import java.awt.event.WindowAdapter +import java.awt.event.WindowEvent +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.ButtonGroup +import javax.swing.JButton +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JRadioButton +import javax.swing.SwingConstants + +/** + * Class representing the TestSparkActionWindow. + * + * @property e The AnActionEvent object. + */ +class TestSparkActionWindow( + private val e: AnActionEvent, + private val visibilityController: VisibilityController, + private val testGenerationController: TestGenerationController, + private val testSparkDisplayManager: TestSparkDisplayManager, + private val testsExecutionResultManager: TestsExecutionResultManager, +) : + JFrame("TestSpark") { + private val project: Project = e.project!! + + private val llmSettingsState: LLMSettingsState + get() = project.getService(LLMSettingsService::class.java).state + private val evoSuiteSettingsState: EvoSuiteSettingsState + get() = project.getService(EvoSuiteSettingsService::class.java).state + + private val llmButton = JRadioButton("${Llm().name}") + private val evoSuiteButton = JRadioButton("${EvoSuite().name}") + private val kexButton = JRadioButton("${Kex().name}") + private val testGeneratorButtonGroup = ButtonGroup() + private val kexForLineCodeTypeErrMsg = JLabel() // The error displayed when if kex and line code type are chosen + + private val psiHelper: PsiHelper + get() { + val file = e.dataContext.getData(CommonDataKeys.PSI_FILE)!! + val psiHelper = PsiHelperProvider.getPsiHelper(file) + return psiHelper!! + } + + private val codeTypes = psiHelper.getCurrentListOfCodeTypes(e) + private val caretOffset: Int = e.dataContext.getData(CommonDataKeys.CARET)?.caretModel?.primaryCaret!!.offset + private val fileUrl = e.dataContext.getData(CommonDataKeys.VIRTUAL_FILE)!!.presentableUrl + + private val codeTypeButtons: MutableList> = mutableListOf() + private val codeTypeButtonGroup = ButtonGroup() + + private val nextButton = JButton(PluginLabelsBundle.get("next")) + + private val cardLayout = CardLayout() + private val llmSetupPanelFactory = LLMSetupPanelBuilder(e, project) + private val llmSampleSelectorFactory = LLMSampleSelectorBuilder(project, psiHelper.language) + private val evoSuitePanelFactory = EvoSuitePanelBuilder(project) + + init { + if (!visibilityController.isVisible) { + visibilityController.isVisible = true + isVisible = true + + val panel = JPanel(cardLayout) + + panel.add(getMainPanel(), "1") + panel.add(createCardPanel(evoSuitePanelFactory), "2") + panel.add(createCardPanel(llmSetupPanelFactory), "3") + + panel.add( + JBScrollPane( + createCardPanel(llmSampleSelectorFactory), + JBScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER, + ), + "4", + ) + + addListeners(panel) + + add(panel) + + pack() + + val dimension: Dimension = Toolkit.getDefaultToolkit().screenSize + val x = (dimension.width - size.width) / 2 + val y = (dimension.height - size.height) / 2 + setLocation(x, y) + } else { + NotificationGroupManager.getInstance() + .getNotificationGroup("Generation Error") + .createNotification( + PluginMessagesBundle.get("generationWindowWarningTitle"), + PluginMessagesBundle.get("generationWindowWarningMessage"), + NotificationType.WARNING, + ) + .notify(e.project) + } + } + + private fun createCardPanel(toolPanelBuilder: PanelBuilder): JPanel { + val cardPanel = JPanel(BorderLayout()) + cardPanel.add(toolPanelBuilder.getTitlePanel(), BorderLayout.NORTH) + cardPanel.add(toolPanelBuilder.getMiddlePanel(), BorderLayout.CENTER) + cardPanel.add(toolPanelBuilder.getBottomPanel(), BorderLayout.SOUTH) + + return cardPanel + } + + /** + * Returns the main panel for the test generator UI. + * This panel contains options for selecting the test generator and the code type. + * It also includes a button for proceeding to the next step. + * + * @return the main panel for the test generator UI + */ + private fun getMainPanel(): JPanel { + val panelTitle = JPanel() + val textTitle = JLabel("Welcome to TestSpark!") + textTitle.font = Font("Monochrome", Font.BOLD, 20) + panelTitle.add(JLabel(TestSparkIcons.pluginIcon)) + panelTitle.add(textTitle) + + if (Llm().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(llmButton) + if (EvoSuite().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(evoSuiteButton) + if (Kex().appliedForLanguage(psiHelper.language)) testGeneratorButtonGroup.add(kexButton) + + val testGeneratorPanel = JPanel() + testGeneratorPanel.add(JLabel("Select the test generator:")) + for (button in testGeneratorButtonGroup.elements) testGeneratorPanel.add(button) + if (testGeneratorButtonGroup.elements.toList().size == 1) { + // A single button is selected by default + testGeneratorButtonGroup.elements.toList()[0].isSelected = true + } + + for ((codeType, codeTypeName) in codeTypes) { + val button = JRadioButton(codeTypeName) + codeTypeButtons.add(codeType to button) + codeTypeButtonGroup.add(button) + } + + val codesToTestPanel = JPanel() + codesToTestPanel.add(JLabel("Select the code type:")) + if (codeTypeButtons.size == 1) { + // A single button is selected by default + codeTypeButtons[0].second.isSelected = true + } + for ((_, button) in codeTypeButtons) codesToTestPanel.add(button) + + val middlePanel = FormBuilder.createFormBuilder() + .setFormLeftIndent(10) + .addComponent( + testGeneratorPanel, + 10, + ) + .addComponent( + codesToTestPanel, + 10, + ) + .panel + + val nextButtonPanel = JPanel() + nextButtonPanel.layout = BoxLayout(nextButtonPanel, BoxLayout.Y_AXIS) + nextButton.isEnabled = false + nextButton.alignmentX = CENTER_ALIGNMENT + kexForLineCodeTypeErrMsg.alignmentX = CENTER_ALIGNMENT + kexForLineCodeTypeErrMsg.horizontalAlignment = SwingConstants.CENTER + nextButtonPanel.add(kexForLineCodeTypeErrMsg) + nextButtonPanel.add(Box.createVerticalStrut(10)) // Add some space between label and button + nextButtonPanel.add(nextButton) + updateNextButton() + + val cardPanel = JPanel(BorderLayout()) + cardPanel.add(panelTitle, BorderLayout.NORTH) + cardPanel.add(middlePanel, BorderLayout.CENTER) + cardPanel.add(nextButtonPanel, BorderLayout.SOUTH) + + return cardPanel + } + + /** + * Adds listeners to various components in the given panel. + * + * @param panel the JPanel to add listeners to + */ + private fun addListeners(panel: JPanel) { + addWindowListener(object : WindowAdapter() { + override fun windowClosing(e: WindowEvent?) { + visibilityController.isVisible = false + } + }) + + llmButton.addActionListener { + updateNextButton() + } + + evoSuiteButton.addActionListener { + updateNextButton() + } + + kexButton.addActionListener { + updateNextButton() + } + + for ((_, button) in codeTypeButtons) { + button.addActionListener { + llmSetupPanelFactory.setPromptEditorType(button.text) + updateNextButton() + } + } + + nextButton.addActionListener { + if (llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected && !llmSettingsState.provideTestSamplesCheckBoxSelected) { + startLLMGeneration() + } else if (llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected) { + cardLayout.next(panel) + cardLayout.next(panel) + cardLayout.next(panel) + pack() + } else if (llmButton.isSelected) { + cardLayout.next(panel) + cardLayout.next(panel) + pack() + } else if (kexButton.isSelected) { + startKexGeneration() + } else if (evoSuiteButton.isSelected && !evoSuiteSettingsState.evosuiteSetupCheckBoxSelected) { + startEvoSuiteGeneration() + } else { + cardLayout.next(panel) + pack() + } + } + + evoSuitePanelFactory.getBackButton().addActionListener { + cardLayout.previous(panel) + pack() + } + + llmSetupPanelFactory.getBackButton().addActionListener { + cardLayout.previous(panel) + cardLayout.previous(panel) + pack() + } + + llmSetupPanelFactory.getFinishedButton().addActionListener { + llmSetupPanelFactory.applyUpdates() + if (llmSettingsState.provideTestSamplesCheckBoxSelected) { + cardLayout.next(panel) + } else { + startLLMGeneration() + } + } + + llmSampleSelectorFactory.getAddButton().addActionListener { + size = Dimension(width, 500) + } + + llmSampleSelectorFactory.getBackButton().addActionListener { + if (llmSettingsState.llmSetupCheckBoxSelected) { + cardLayout.previous(panel) + } else { + cardLayout.previous(panel) + cardLayout.previous(panel) + cardLayout.previous(panel) + } + pack() + } + + llmSampleSelectorFactory.getFinishedButton().addActionListener { + llmSampleSelectorFactory.applyUpdates() + startLLMGeneration() + } + + evoSuitePanelFactory.getFinishedButton().addActionListener { + evoSuitePanelFactory.applyUpdates() + startEvoSuiteGeneration() + } + } + + private fun startUnitTestGenerationTool(tool: Tool) { + if (!testGenerationController.isGeneratorRunning(project)) { + val testSamplesCode = llmSampleSelectorFactory.getTestSamplesCode() + + for ((codeType, button) in codeTypeButtons) { + if (button.isSelected) { + when (codeType) { + CodeType.CLASS -> tool.generateTestsForClass( + project, + psiHelper, + caretOffset, + fileUrl, + testSamplesCode, + testGenerationController, + testSparkDisplayManager, + testsExecutionResultManager, + ) + + CodeType.METHOD -> tool.generateTestsForMethod( + project, + psiHelper, + caretOffset, + fileUrl, + testSamplesCode, + testGenerationController, + testSparkDisplayManager, + testsExecutionResultManager, + ) + + CodeType.LINE -> tool.generateTestsForLine( + project, + psiHelper, + caretOffset, + fileUrl, + testSamplesCode, + testGenerationController, + testSparkDisplayManager, + testsExecutionResultManager, + ) + } + break + } + } + } + + visibilityController.isVisible = false + dispose() + } + + private fun startKexGeneration() = startUnitTestGenerationTool(tool = Kex()) + private fun startEvoSuiteGeneration() = startUnitTestGenerationTool(tool = EvoSuite()) + private fun startLLMGeneration() = startUnitTestGenerationTool(tool = Llm()) + + /** + * Updates the state of the "Next" button based on the selected options. + * The "Next" button is enabled only if a test generator button (llmButton or evoSuiteButton) and at least one + * code type button (from codeTypeButtons) are selected. + * + * This method should be called whenever the mentioned above buttons are clicked. + */ + private fun updateNextButton() { + val isTestGeneratorButtonGroupSelected = + llmButton.isSelected || evoSuiteButton.isSelected || kexButton.isSelected + val isCodeTypeButtonGroupSelected = codeTypeButtons.any { it.second.isSelected } + val kexForCodeLineType = + kexButton.isSelected && codeTypeButtons.any { (codeType, button) -> codeType == CodeType.LINE && button.isSelected } + if (kexForCodeLineType) { + kexForLineCodeTypeErrMsg.text = + "* Kex cannot generate tests for a single line. Please change your selection" + } else { + kexForLineCodeTypeErrMsg.text = "" + } + + nextButton.isEnabled = + isTestGeneratorButtonGroupSelected && isCodeTypeButtonGroupSelected && !kexForCodeLineType + + if ((llmButton.isSelected && !llmSettingsState.llmSetupCheckBoxSelected && !llmSettingsState.provideTestSamplesCheckBoxSelected) || + (evoSuiteButton.isSelected && !evoSuiteSettingsState.evosuiteSetupCheckBoxSelected) || + kexButton.isSelected + ) { + nextButton.text = PluginLabelsBundle.get("ok") + } else { + nextButton.text = PluginLabelsBundle.get("next") + } + } +} diff --git a/src/main/kotlin/org/jetbrains/research/testspark/tools/factories/TestCompilerFactory.kt b/src/main/kotlin/org/jetbrains/research/testspark/tools/factories/TestCompilerFactory.kt index 025761b24..62a5d14ea 100644 --- a/src/main/kotlin/org/jetbrains/research/testspark/tools/factories/TestCompilerFactory.kt +++ b/src/main/kotlin/org/jetbrains/research/testspark/tools/factories/TestCompilerFactory.kt @@ -17,20 +17,17 @@ object TestCompilerFactory { project: Project, junitVersion: JUnitVersion, language: SupportedLanguage, - javaHomeDirectory: String? = null, + javaSDKHomePath: String = findJavaSDKHomePath(project) ): TestCompiler { val libraryPaths = LibraryPathsProvider.getTestCompilationLibraryPaths() val junitLibraryPaths = LibraryPathsProvider.getJUnitLibraryPaths(junitVersion) - // TODO add the warning window that for Java we always need the javaHomeDirectoryPath return when (language) { SupportedLanguage.Java -> { - val javaSDKHomePath = findJavaSDKHomePath(javaHomeDirectory, project) JavaTestCompiler(libraryPaths, junitLibraryPaths, javaSDKHomePath) } SupportedLanguage.Kotlin -> { // Kotlinc relies on java to compile kotlin files. - val javaSDKHomePath = findJavaSDKHomePath(javaHomeDirectory, project) // kotlinc should be under `[kotlinSDKHomeDirectory]/bin/kotlinc` val kotlinSDKHomeDirectory = KotlinPluginLayout.kotlinc.absolutePath KotlinTestCompiler(libraryPaths, junitLibraryPaths, kotlinSDKHomeDirectory, javaSDKHomePath) @@ -41,26 +38,16 @@ object TestCompilerFactory { /** * Finds the home path of the Java SDK. * - * @param javaHomeDirectory The directory where Java SDK is installed. If null, the project's configured SDK path is used. * @param project The project for which the Java SDK home path is being determined. * @return The home path of the Java SDK. * @throws JavaSDKMissingException If no Java SDK is configured for the project. */ - private fun findJavaSDKHomePath( - javaHomeDirectory: String?, - project: Project, - ): String { - val javaSDKHomePath = - javaHomeDirectory - ?: ProjectRootManager - .getInstance(project) - .projectSdk - ?.homeDirectory - ?.path - - if (javaSDKHomePath == null) { - throw JavaSDKMissingException(LLMMessagesBundle.get("javaSdkNotConfigured")) - } - return javaSDKHomePath + private fun findJavaSDKHomePath(project: Project): String { + return ProjectRootManager + .getInstance(project) + .projectSdk + ?.homeDirectory + ?.path + ?: (throw JavaSDKMissingException(LLMMessagesBundle.get("javaSdkNotConfigured"))) } }