diff --git a/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar b/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar new file mode 100644 index 0000000..6c6016a Binary files /dev/null and b/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/src/main/java/org/testshift/testcube/amplify/AmplifyTestMarkerContributor.java b/src/main/java/org/testshift/testcube/amplify/AmplifyTestMarkerContributor.java index 839ecc9..75bb89f 100644 --- a/src/main/java/org/testshift/testcube/amplify/AmplifyTestMarkerContributor.java +++ b/src/main/java/org/testshift/testcube/amplify/AmplifyTestMarkerContributor.java @@ -2,20 +2,24 @@ import com.intellij.codeInsight.TestFrameworks; import com.intellij.execution.lineMarker.RunLineMarkerContributor; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethod; +import com.intellij.psi.impl.source.PsiClassImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testIntegration.TestFramework; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.testshift.testcube.icons.TestCubeIcons; +import com.intellij.testIntegration.TestFinderHelper; +import com.intellij.testIntegration.LanguageTestCreators; -import java.util.Objects; +import java.util.*; public class AmplifyTestMarkerContributor extends RunLineMarkerContributor { @@ -41,14 +45,41 @@ public class AmplifyTestMarkerContributor extends RunLineMarkerContributor { */ PsiClass containingClass = PsiTreeUtil.getParentOfType(parent, PsiClass.class); if (!isTestMethod(containingClass, (PsiMethod) parent)) { -// return null; - String targetMethodName = ((PsiMethod)parent).getName(); - String targetClassName = Objects.requireNonNull(((PsiMethod) parent).getContainingClass()) - .getQualifiedName(); + PsiClass targetClass = Objects.requireNonNull(((PsiMethod) parent).getContainingClass()); - return new Info(TestCubeIcons.AMPLIFY_TEST, tooltipProvider, - new ShowCFGAction("generate test " + - "cases for '" + element.getText() +"()'", targetMethodName, targetClassName)); +// //the selected class +// PsiElement sourceElement = TestFinderHelper.findSourceElement(element); +// //the test class +// Collection testClasses = ReadAction.compute(() -> TestFinderHelper.findTestsForClass(sourceElement)); +// final List candidates = Collections.synchronizedList(new ArrayList<>()); +// candidates.addAll(testClasses); +// PsiClass testClass = null; +// PsiMethod testMethod = null; +// // test class exist +// if(candidates.size()>0) { +// testClass = (PsiClass) candidates.get(0); +// PsiMethod[] testMethods = testClass.getAllMethods(); +// // consider use which method as Start or all +// if(testMethods.length>0) { +// testMethod = testMethods[0]; +// } +// } + + VirtualFile file = parent.getContainingFile().getVirtualFile(); + if (file != null) { + VirtualFile moduleRoot = ProjectFileIndex.SERVICE.getInstance(parent.getProject()) + .getContentRootForFile(file); + String moduleRootPath; + if (moduleRoot == null) { + moduleRootPath = parent.getProject().getBasePath(); + } else { + moduleRootPath = moduleRoot.getPath(); + } + return new Info(TestCubeIcons.AMPLIFY_TEST, tooltipProvider, + new ShowCFGAction("generate test " + + "cases for '" + element.getText() +"()'", targetClass, + (PsiMethod)parent, moduleRootPath)); + } } // test method String testMethodName = ((PsiMethod) parent).getName(); diff --git a/src/main/java/org/testshift/testcube/amplify/ShowCFGAction.java b/src/main/java/org/testshift/testcube/amplify/ShowCFGAction.java index b56bc8a..b1a5035 100644 --- a/src/main/java/org/testshift/testcube/amplify/ShowCFGAction.java +++ b/src/main/java/org/testshift/testcube/amplify/ShowCFGAction.java @@ -1,46 +1,81 @@ package org.testshift.testcube.amplify; +import com.intellij.codeInsight.navigation.GotoTargetHandler; +import com.intellij.icons.AllIcons; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.lang.LangBundle; +import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.testIntegration.LanguageTestCreators; +import com.intellij.testIntegration.TestCreator; +import com.intellij.testIntegration.TestFinderHelper; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; +import com.intellij.util.ObjectUtils; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -//import org.plantuml.idea.external.PlantUmlFacade; -//import org.plantuml.idea.plantuml.ImageFormat; -//import org.plantuml.idea.preview.Zoom; -//import org.plantuml.idea.rendering.RenderCacheItem; -//import org.plantuml.idea.rendering.RenderCommand; -//import org.plantuml.idea.rendering.RenderRequest; -//import org.plantuml.idea.rendering.RenderResult; -//import org.plantuml.idea.settings.PlantUmlSettings; -import org.testshift.testcube.branches.CFGPanel; -import org.testshift.testcube.branches.NoBranchDialog; +import eu.stamp_project.dspot.common.report.output.selector.TestClassJSON; +import org.testshift.testcube.branches.*; import org.testshift.testcube.branches.rendering.*; import org.testshift.testcube.icons.TestCubeIcons; -//import org.plantuml.idea.adapter.FacadeImpl; +import org.testshift.testcube.inspect.InspectTestCubeResultsAction; +import org.testshift.testcube.misc.Config; +import org.testshift.testcube.misc.TestCubeNotifier; +import org.testshift.testcube.misc.Util; +import org.testshift.testcube.settings.AppSettingsState; +import org.testshift.testcube.settings.AskJavaPathDialogWrapper; +import org.testshift.testcube.settings.AskMavenHomeDialogWrapper; +import javax.swing.*; import java.awt.*; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.List; public class ShowCFGAction extends AnAction { private static final Logger logger = Logger.getInstance(ShowCFGAction.class); - private final String targetClass; - private final String targetMethod; + private final PsiClass targetClass; + private final PsiMethod targetMethod; + private PsiClass testClass; + private String testMethodsName; + private Project project; + private String moduleRootPath; - public ShowCFGAction(@Nullable @Nls(capitalization = Nls.Capitalization.Title) String text, - String targetClass, - String targetMethod) { + public ShowCFGAction(@Nullable /*@Nls(capitalization = Nls.Capitalization.Title)*/ String text, + PsiClass targetClass, PsiMethod targetMethod, String moduleRootPath) { super(text, "generate test cases for the selected method", TestCubeIcons.AMPLIFY_TEST); + this.moduleRootPath = moduleRootPath; this.targetClass = targetClass; this.targetMethod = targetMethod; } @@ -54,63 +89,261 @@ public void update(AnActionEvent e) { @Override public void actionPerformed(@NotNull AnActionEvent event) { - // TODO:transform code to uml, return uml and branch number - int branchNum = 1; - if(branchNum==0){ - NoBranchDialog dialog = new NoBranchDialog(); - dialog.pack(); - dialog.setVisible(true); - } else{ - - ImageFormat imageFormat = ImageFormat.PNG; - int page = 0; - int version=0; - String sourceFilePath = "F:\\mytestcube\\test-cube\\src\\main\\resources\\test.puml"; - - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(sourceFilePath)); - } catch (FileNotFoundException e) { - e.printStackTrace(); + this.project = event.getProject(); + //find testclass + Collection testClasses = ReadAction.compute(() -> TestFinderHelper.findTestsForClass(targetClass)); + final List candidates = Collections.synchronizedList(new ArrayList<>()); + candidates.addAll(testClasses); + if(candidates.size()>0) { + this.testClass = (PsiClass) candidates.get(0); + } + + if(this.testClass==null){ + Editor editor = event.getData(CommonDataKeys.EDITOR); + if (editor == null || project == null) return; + PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project); + if (file == null) return; + CreateTestDialog createTestDialog = new CreateTestDialog(editor, file); + createTestDialog.pack(); + createTestDialog.setVisible(true); + + return; + } + +// try { +// Thread.currentThread().sleep(5000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } + // find testMethods; + //TODO: make use of all testMethods + PsiMethod[] testMethods = this.testClass.getMethods(); + + if(testMethods.length ==0 || isEmptyMethods(testMethods)){ + WriteObjectDialog writeObjectDialog = new WriteObjectDialog(); + writeObjectDialog.pack(); + writeObjectDialog.setVisible(true); + return; + } + + this.testMethodsName = getMethodsString(testMethods); + + + try { + runDSpotForCoverage(this.project); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } +// TestCubeNotifier notifier = new TestCubeNotifier(); +// notifier.notify(project, "Initial Coverage computing finished", +// new ShowCFGCoverageAction(project, targetClass, targetMethod, testClass, testMethodsName, +// moduleRootPath)); + } + + private String getMethodsString(PsiMethod[] testMethods) { + String methods = ""; + for(PsiMethod method: testMethods){ + methods = methods + method.getName() +","; + } + methods = methods.substring(0,methods.length()-1); + return methods; + } + + private boolean isEmptyMethods(PsiMethod[] testMethods) { + boolean empty = true; + for(PsiMethod method: testMethods){ + if(!method.getBody().isEmpty()){ + empty = false; + break; } - StringBuilder stringBuilder = new StringBuilder(); - String line = null; - String ls = System.getProperty("line.separator"); - while (true) { + } + return empty; + } + + private void runDSpotForCoverage(Project currentProject) throws IOException, InterruptedException { + + IdeaPluginDescriptor testCubePlugin = PluginManagerCore.getPlugin(PluginId.getId("org.testshift.testcube")); + if (testCubePlugin != null) { + testCubePlugin.getPluginClassLoader(); + } + + Sdk projectSdk = ProjectRootManager.getInstance(currentProject).getProjectSdk(); + + if (projectSdk != null) { + AppSettingsState.getInstance().java8Path = projectSdk.getHomePath(); + } + + if (AppSettingsState.getInstance().java8Path.isEmpty()) { + AskJavaPathDialogWrapper dialog = new AskJavaPathDialogWrapper(); + dialog.showAndGet(); + boolean pathValid = dialog.setJavaPathIfValid(); + // todo handle non valid + } + + // check if Gradle or Maven + boolean isGradle = ExternalSystemApiUtil.isExternalSystemAwareModule(new ProjectSystemId("GRADLE"), + ModuleManager.getInstance(currentProject) + .getModules()[0]); + boolean isMaven = ExternalSystemApiUtil.isExternalSystemAwareModule(new ProjectSystemId("Maven"), + ModuleManager.getInstance(currentProject) + .getModules()[0]); + + String relativePathToClasses = ""; + String relativePathToTestClasses = ""; + String relativePathToSourceCode = ""; + String relativePathToTestCode = ""; + String automaticBuilder = ""; + if (isMaven) { + relativePathToClasses = "target" + File.separator + "classes" + File.separator; + relativePathToTestClasses = "target" + File.separator + "test-classes" + File.separator; + relativePathToSourceCode = "src" + File.separator + "main" + File.separator + "java" + File.separator; + relativePathToTestCode = "src" + File.separator + "test" + File.separator + "java" + File.separator; + automaticBuilder = "Maven"; + } else { + relativePathToClasses = "bin" + File.separator + "main" + File.separator; + relativePathToTestClasses = "bin" + File.separator + "test" + File.separator; + automaticBuilder = "Gradle"; + if (!isGradle) { + logger.info("Neither Gradle nor Maven"); + } + } + + if (isMaven && AppSettingsState.getInstance().mavenHome.isEmpty()) { + AskMavenHomeDialogWrapper dialog = new AskMavenHomeDialogWrapper(); + dialog.showAndGet(); + boolean mavenHomeValid = dialog.setMavenHomeIfValid(); + // todo handle non valid + } + + String javaHome = AppSettingsState.getInstance().java8Path; + String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; + + String pluginPath = PathManager.getPluginsPath(); + String dSpotPath = pluginPath + File.separator + "test-cube" + File.separator + "lib" + File.separator + + "dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar"; + + String finalRelativePathToClasses = relativePathToClasses; + String finalRelativePathToTestClasses = relativePathToTestClasses; +// String finalRelativePathToSourceCode = relativePathToSourceCode; +// String finalRelativePathToTestCode = relativePathToTestCode; + String finalAutomaticBuilder = automaticBuilder; + + String testClassName = testClass.getQualifiedName(); + String targetClassName = targetClass.getQualifiedName(); + String targetMethodName = targetMethod.getName(); + Task.Backgroundable dspotTask = new Task.Backgroundable(currentProject, "Computing coverage", true) { + public void run(@NotNull ProgressIndicator indicator) { + // clean output directory + // todo close open amplification result windows or split output into different directories try { - if (!((line = reader.readLine()) != null)) break; + File outputDirectory = new File(Util.getDSpotOutputPath(currentProject)); + if (outputDirectory.exists()) { + FileUtils.cleanDirectory(outputDirectory); + } } catch (IOException e) { e.printStackTrace(); } - stringBuilder.append(line); - stringBuilder.append(ls); - } - stringBuilder.deleteCharAt(stringBuilder.length() - 1); - try { - reader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - String source = stringBuilder.toString(); + String targetModule = ""; + if (isMaven) { + targetModule = moduleRootPath.replace(currentProject.getBasePath() + "/", ""); + } - RenderCommand.Reason reason = RenderCommand.Reason.FILE_SWITCHED; + // @formatter:off + List dSpotStarter = new ArrayList<>(Arrays.asList(javaBin, "-jar", dSpotPath, + "--absolute-path-to-project-root", currentProject.getBasePath(), + "--relative-path-to-classes", finalRelativePathToClasses, + "--relative-path-to-test-classes", finalRelativePathToTestClasses, +// "--relative-path-to-source-code", finalRelativePathToSourceCode, +// "--relative-path-to-test-code", finalRelativePathToTestCode, + "--test-criterion", "BranchCoverageSelector", + "--amplifiers", "TargetMethodAdderOnExistingObjectsAmplifier", +// "--input-ampl-distributor", "RandomInputAmplDistributor", + "--iteration", "1", + "--test", testClassName, + // TODO handlle null on testMethod + "--test-cases", testMethodsName, + "--target-class", targetClassName, + "--target-method", targetMethodName, + "--output-directory", Util.getDSpotOutputPath(currentProject), +// "--max-test-amplified", "25", + "--automatic-builder", finalAutomaticBuilder, + //"--generate-new-test-class", + //"--keep-original-test-methods", + "--verbose", + "--dev-friendly", + "--clean", + "--with-comment=All")); + // @formatter:on - CFGPanel cfgPanel = new CFGPanel(sourceFilePath, source, imageFormat, page, version); +// if (!AppSettingsState.getInstance().generateAssertions) { +// dSpotStarter.add("--only-input-amplification"); +// } + @NotNull Module[] modules = ModuleManager.getInstance(currentProject).getModules(); + if (modules.length > 1) { + dSpotStarter.add("--target-module"); + dSpotStarter.add(targetModule); + } - cfgPanel.render(reason); - cfgPanel.displayResult(reason); + ProcessBuilder pb = new ProcessBuilder(dSpotStarter); - cfgPanel.setLayout(new GridLayout()); - ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); - Content content = contentFactory.createContent(cfgPanel, "CFG", false); - content.setCloseable(true); - content.setIcon(TestCubeIcons.AMPLIFY_TEST); - ToolWindow toolWindow = ToolWindowManager.getInstance(event.getProject()).getToolWindow("Test Cube"); - toolWindow.getContentManager().addContent(content); - toolWindow.getContentManager().setSelectedContent(content); + pb.environment().put("MAVEN_HOME", AppSettingsState.getInstance().mavenHome); - toolWindow.show(); - } + File workdir = new File(Util.getTestCubeOutputPath(currentProject) + File.separator + "workdir"); + if (!workdir.exists()) { + if (!workdir.mkdirs()) { + logger.error("Could not create workdir output directory!"); + } + } + File workdirTarget = new File(workdir.getPath() + File.separator + "target" + File.separator + "dspot"); + if (!workdirTarget.exists()) { + if (!workdirTarget.mkdirs()) { + logger.error("Could not create workdir/target/dspot output directory!"); + } + } + pb.directory(workdir); + + pb.redirectErrorStream(true); + try { + Process p = pb.start(); + + File outputDir = new File(Util.getTestCubeOutputPath(currentProject)); + if (!outputDir.exists()) { + if (!outputDir.mkdirs()) { + logger.error("Could not create Test Cube output directory!"); + } + } + + File dSpotTerminalOutput = new File( + Util.getTestCubeOutputPath(currentProject) + File.separator + "terminal_output_dspot.txt"); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(dSpotTerminalOutput))) { + InputStream is = p.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + for (String line = br.readLine(); line != null; line = br.readLine()) { + System.out.println(line); + writer.write(line); + writer.newLine(); + } + } + p.waitFor(); + System.out.println(p.exitValue()); + + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + } + + Util.sleepAndRefreshProject(currentProject); + + TestCubeNotifier notifier = new TestCubeNotifier(); + notifier.notify(currentProject, "Initial Coverage computing finished", + new ShowCFGCoverageAction(project, targetClass, targetMethod, testClass, + testMethodsName, + moduleRootPath)); + } + }; + + BackgroundableProcessIndicator processIndicator = new BackgroundableProcessIndicator(dspotTask); + ProgressManager.getInstance().runProcessWithProgressAsynchronously(dspotTask, processIndicator); } } diff --git a/src/main/java/org/testshift/testcube/amplify/ShowCFGCoverageAction.java b/src/main/java/org/testshift/testcube/amplify/ShowCFGCoverageAction.java new file mode 100644 index 0000000..d885e12 --- /dev/null +++ b/src/main/java/org/testshift/testcube/amplify/ShowCFGCoverageAction.java @@ -0,0 +1,110 @@ +package org.testshift.testcube.amplify; + +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentFactory; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import org.jetbrains.annotations.NotNull; +import org.testshift.testcube.branches.AllCoveredDialog; +import org.testshift.testcube.branches.CFGWindow; +import org.testshift.testcube.branches.CodeToUml; +import org.testshift.testcube.branches.NoBranchDialog; +import org.testshift.testcube.icons.TestCubeIcons; +import org.testshift.testcube.misc.Util; + +import java.util.Map; +import java.util.Set; + +public class ShowCFGCoverageAction extends NotificationAction { + private final Project project; + private final PsiClass targetClass; + private final PsiMethod targetMethod; + private PsiClass testClass; + private String testMethods; + private String moduleRootPath; + + public ShowCFGCoverageAction(Project project, PsiClass targetClass, PsiMethod targetMethod, PsiClass testClass, + String testMethods, String moduleRootPath) { + super("Inspect Control Flow Graph with Coverage"); + this.project = project; + this.targetClass = targetClass; + this.targetMethod = targetMethod; + this.testClass = testClass; + this.testMethods = testMethods; + this.moduleRootPath = moduleRootPath; + } + + @Override + public void update(AnActionEvent e) { + // Set the availability based on whether a project is open + Project project = e.getProject(); + e.getPresentation().setEnabledAndVisible(project != null); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent event, @NotNull Notification notification) { + TestClassBranchCoverageJSON coverageResult = (TestClassBranchCoverageJSON) Util.getBranchCoverageJSON(project, + testClass.getQualifiedName()); + Set initialCoveredLines = Util.getInitialCoveredLine(coverageResult); + Set initialCoveredBranches = Util.getInitialCoveredBranch(coverageResult); + + String targetMethodText = targetMethod.getBody().getText(); + PsiFile containingFile = targetMethod.getContainingFile(); + Project project = containingFile.getProject(); + PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); + Document document = psiDocumentManager.getDocument(containingFile); + int textOffset = targetMethod.getBody().getTextOffset(); + int lineNumber = document.getLineNumber(textOffset); // this lineNumber +1 = starlinenumber + + + Map result = CodeToUml.codeToUml(targetMethodText, lineNumber + 1); + String source = result.keySet().toArray()[0].toString(); + int branchNum = result.get(source); + + if (branchNum == 0) { + if(initialCoveredLines.isEmpty()) { + NoBranchDialog dialog = new NoBranchDialog(); + dialog.pack(); + dialog.setVisible(true); + } + else{ + AllCoveredDialog dialog = new AllCoveredDialog(); + dialog.pack();; + dialog.setVisible(true); + } + } + + String targetClassName = targetClass.getQualifiedName(); + String targetMethodName = targetMethod.getName(); + String testClassName = testClass.getQualifiedName(); + + CFGWindow cfgWindow = new CFGWindow(project, targetClassName, targetMethodName, source, initialCoveredLines, + initialCoveredBranches, moduleRootPath, testClassName, testMethods, + branchNum); + + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Test Cube"); + + if (toolWindow != null) { + ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); + Content content = contentFactory.createContent(cfgWindow.getContent(), cfgWindow.getDisplayName(), false); + content.setCloseable(true); + content.setIcon(TestCubeIcons.AMPLIFY_TEST); + + toolWindow.getContentManager().addContent(content); + toolWindow.getContentManager().setSelectedContent(content); + + toolWindow.show(); + } + } + +} diff --git a/src/main/java/org/testshift/testcube/amplify/StartTestCubeAction.java b/src/main/java/org/testshift/testcube/amplify/StartTestCubeAction.java index 61e8382..0c18129 100644 --- a/src/main/java/org/testshift/testcube/amplify/StartTestCubeAction.java +++ b/src/main/java/org/testshift/testcube/amplify/StartTestCubeAction.java @@ -48,7 +48,7 @@ public class StartTestCubeAction extends AnAction { public StartTestCubeAction() { } - public StartTestCubeAction(@Nullable @Nls(capitalization = Nls.Capitalization.Title) String text, + public StartTestCubeAction(@Nullable /*@Nls(capitalization = Nls.Capitalization.Title)*/ String text, @NotNull String testClass, @Nullable("null means whole class") String testMethod, String moduleRootPath) { super(text, "Improves the selected test case by applying amplification operators", TestCubeIcons.AMPLIFY_TEST); diff --git a/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.form b/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.form new file mode 100644 index 0000000..4331162 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.form @@ -0,0 +1,72 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.java b/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.java new file mode 100644 index 0000000..fac9510 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/AllCoveredDialog.java @@ -0,0 +1,60 @@ +package org.testshift.testcube.branches; + +import javax.swing.*; +import java.awt.event.*; + +public class AllCoveredDialog extends JDialog { + private JPanel contentPane; + private JButton buttonOK; + private JButton buttonCancel; + private JLabel Content; + + public AllCoveredDialog() { + setContentPane(contentPane); + setLocationRelativeTo(null); + setModal(false); + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + buttonCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + + private void onOK() { + dispose(); + } + + private void onCancel() { + // add your code here if necessary + dispose(); + } + + public static void main(String[] args) { + NoBranchDialog dialog = new NoBranchDialog(); + dialog.pack(); + dialog.setVisible(true); + dialog.setSize(800,400); + } +} diff --git a/src/main/java/org/testshift/testcube/branches/CFGPanel.java b/src/main/java/org/testshift/testcube/branches/CFGPanel.java index 648230b..1197786 100644 --- a/src/main/java/org/testshift/testcube/branches/CFGPanel.java +++ b/src/main/java/org/testshift/testcube/branches/CFGPanel.java @@ -1,28 +1,56 @@ package org.testshift.testcube.branches; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.colorpicker.ButtonPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.Alarm; +import eu.stamp_project.dspot.common.report.output.selector.extendedcoverage.json.TestClassJSON; import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.FileFormatOption; +import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; +import org.testshift.testcube.amplify.InspectDSpotTerminalOutputAction; import org.testshift.testcube.branches.actions.ZoomAction; import org.testshift.testcube.branches.preview.image.ImageContainerPng; //import org.testshift.testcube.branches.preview.image.ImageContainerSvg; import org.testshift.testcube.branches.preview.image.links.Highlighter; import org.testshift.testcube.branches.preview.image.links.MyJLabel; import org.testshift.testcube.branches.rendering.*; +import org.testshift.testcube.inspect.InspectTestCubeResultsAction; +import org.testshift.testcube.misc.Config; +import org.testshift.testcube.misc.TestCubeNotifier; +import org.testshift.testcube.misc.Util; +import org.testshift.testcube.settings.AppSettingsState; +import org.testshift.testcube.settings.AskJavaPathDialogWrapper; +import org.testshift.testcube.settings.AskMavenHomeDialogWrapper; import javax.swing.*; import java.awt.*; import java.awt.event.*; +import java.io.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; public class CFGPanel extends JPanel implements Disposable{ @@ -39,23 +67,29 @@ public class CFGPanel extends JPanel implements Disposable{ private Highlighter highlighter; private int selectedPage = -1; // private RenderRequest renderRequest; - private String sourceFilePath; +// private String sourceFilePath; private String source; private ImageFormat imageFormat; private int page; private int version; - private List hilightText; - private JPanel buttonPanel; - private JButton finish; + private String hilightText = ""; + private Set initialCoveredLines; + private Set initialCoveredBranches; - public CFGPanel(String sourceFilePath, String source, ImageFormat imageFormat, int page, int version){ + private Set newCoveredLines; + private Set newCoveredBranches; + + public CFGPanel(/*String sourceFilePath,*/ String source, ImageFormat imageFormat, int page, int version, + Set initialCoveredLines, Set initialCoveredBranches){ this.source = source; - this.sourceFilePath = sourceFilePath; +// this.sourceFilePath = sourceFilePath; this.imageFormat = imageFormat; this.page = page; this.version = version; + this.initialCoveredLines = initialCoveredLines; + this.initialCoveredBranches = initialCoveredBranches; zoom = new Zoom(this,100, false); - hilightText = new ArrayList<>(); +// List< String> hilightText = new ArrayList<>(); setupUI(); // LowMemoryWatcher.register(new Runnable() { // @Override @@ -74,9 +108,23 @@ public CFGPanel(String sourceFilePath, String source, ImageFormat imageFormat, i backgroundZoomAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); } + public CFGPanel(CFGPanel cfgPanel){ + this.source = cfgPanel.source; +// this.sourceFilePath = sourceFilePath; + this.imageFormat = cfgPanel.imageFormat; + this.page = cfgPanel.page; + this.version = cfgPanel.version; + this.initialCoveredLines = cfgPanel.initialCoveredLines; + this.initialCoveredBranches = cfgPanel.initialCoveredBranches; + zoom = new Zoom(this,100, false); + setupUI(); + highlighter = new Highlighter(); + backgroundZoomAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); + } + private void setupUI() { //好像没用 - createToolbar(); +// createToolbar(); imagesPanel = new JPanel(); imagesPanel.setLayout(new BoxLayout(imagesPanel, BoxLayout.Y_AXIS)); @@ -114,13 +162,9 @@ public void adjustmentValueChanged(AdjustmentEvent adjustmentEvent) { addScrollBarListeners(imagesPanel); } - private void finish() { - //get startTestcubeAction -// this.dispose(); - } - protected void createToolbar() { - } +// protected void createToolbar() { +// } public RenderCacheItem getDisplayedItem() { return displayedItem; @@ -197,6 +241,9 @@ public void changeZoom(int unscaledZoom, Point point) { this.render(reason); this.displayResult(reason); this.maintainHighlight(); + this.maintainInitialCover(); + this.maintainNewCover(); + // renderRequest.disableSvgZoom(); //show the zoom ratio // WindowManager.getInstance().getStatusBar(project).setInfo("Zoomed changed to " + unscaledZoom + "%"); @@ -216,7 +263,7 @@ public static java.util.List getAllComponents(final Container c) { public void render(RenderCommand.Reason reason){ RenderCacheItem cachedItem = null; - RenderRequest renderRequest = new RenderRequest(sourceFilePath, source, imageFormat, page, zoom, version, + RenderRequest renderRequest = new RenderRequest(/*sourceFilePath,*/ source, imageFormat, page, zoom, version, true, reason); FacadeImpl plantUmlFacade = new FacadeImpl(); RenderResult render = plantUmlFacade.render(renderRequest, cachedItem); @@ -224,7 +271,7 @@ public void render(RenderCommand.Reason reason){ } public void displayResult(RenderCommand.Reason reason){; - RenderRequest renderRequest = new RenderRequest(sourceFilePath, source, imageFormat, page, zoom, version, + RenderRequest renderRequest = new RenderRequest(/*sourceFilePath,*/ source, imageFormat, page, zoom, version, true, reason); RenderCacheItem newRenderCacheItem = new RenderCacheItem(renderRequest, this.renderResult, page, version); displayResult(newRenderCacheItem); @@ -235,16 +282,16 @@ public void displayResult(RenderCacheItem newItem) { } private boolean displayImages(RenderCacheItem cacheItem, boolean force) { - RenderCacheItem displayedItem = this.displayedItem; +// RenderCacheItem displayedItem = this.displayedItem; //must be before revalidate - int lastValidVerticalScrollValue = this.lastValidVerticalScrollValue; - int lastValidHorizontalScrollValue = this.lastValidHorizontalScrollValue; +// int lastValidVerticalScrollValue = this.lastValidVerticalScrollValue; +// int lastValidHorizontalScrollValue = this.lastValidHorizontalScrollValue; this.displayedItem = cacheItem; ImageItem[] imageItems = cacheItem.getImageItems(); - RenderResult renderResult = cacheItem.getRenderResult(); +// RenderResult renderResult = cacheItem.getRenderResult(); int requestedPage = cacheItem.getRequestedPage(); removeAllImages(); @@ -257,14 +304,14 @@ private boolean displayImages(RenderCacheItem cacheItem, boolean force) { } public void recordHilight(){ - hilightText.clear(); for(Component component: imagesPanel.getComponents()) { if(component instanceof ImageContainerPng){ for( Component label : ((ImageContainerPng) component).getComponents()){ - if(label instanceof MyJLabel){ + if(label instanceof MyJLabel && ((MyJLabel) label).isBranch()){ if(((MyJLabel) label).isHighlighted()){ - hilightText.add(((MyJLabel) label).getLinkDataText()); + hilightText=((MyJLabel) label).getLinkDataText(); + break; } } } @@ -273,9 +320,17 @@ public void recordHilight(){ } private void maintainHighlight(){ - for(String text: hilightText){ - new Highlighter().highlightImages(imagesPanel, text); - } +// for(String text: hilightText){ + highlighter.highlightImages(imagesPanel, hilightText); +// } + } + + public void maintainInitialCover(){ + highlighter.coverInitialLinesAndBranches(imagesPanel, initialCoveredLines, initialCoveredBranches); + } + + public void maintainNewCover(){ + highlighter.coverNewLinesAndBranches(imagesPanel, newCoveredLines, newCoveredBranches); } private void removeAllImages() { @@ -293,13 +348,9 @@ private void displayImage(RenderCacheItem cacheItem, int pageNumber, ImageItem i imagesPanel.add(component); imagesPanel.add(separator()); - buttonPanel = new JPanel(); - finish = new JButton("Finish"); - finish.addActionListener(l->finish()); - buttonPanel.add(finish, BorderLayout.SOUTH); - imagesPanel.add(buttonPanel); } + @NotNull private JComponent createImageContainer(RenderCacheItem cacheItem, int pageNumber, ImageItem imageWithData) { if (imageWithData == null) { @@ -325,9 +376,31 @@ public JPanel getImagesPanel() { return imagesPanel; } + public String getHilightText() { + return hilightText; + } + + public Set getInitialCoveredLines() { + return initialCoveredLines; + } + + public Set getInitialCoveredBranches() { + return initialCoveredBranches; + } + + public void setNewCoveredLines(Set newCoveredLines) { + this.newCoveredLines = newCoveredLines; + } + + public void setNewCoveredBranches(Set newCoveredBranches) { + this.newCoveredBranches = newCoveredBranches; + } + @Override public void dispose() { // logger.debug("dispose"); removeAllImages(); } + + } diff --git a/src/main/java/org/testshift/testcube/branches/CFGWindow.java b/src/main/java/org/testshift/testcube/branches/CFGWindow.java new file mode 100644 index 0000000..89fb9e7 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/CFGWindow.java @@ -0,0 +1,371 @@ +package org.testshift.testcube.branches; + +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import eu.stamp_project.dspot.common.report.output.selector.extendedcoverage.json.TestClassJSON; +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.testshift.testcube.amplify.InspectDSpotTerminalOutputAction; +import org.testshift.testcube.amplify.StartTestCubeAction; +import org.testshift.testcube.branches.rendering.ImageFormat; +import org.testshift.testcube.branches.rendering.RenderCommand; +import org.testshift.testcube.inspect.InspectResultWithCFGAction; +import org.testshift.testcube.inspect.InspectTestCubeResultsAction; +import org.testshift.testcube.misc.Config; +import org.testshift.testcube.misc.TestCubeNotifier; +import org.testshift.testcube.misc.Util; +import org.testshift.testcube.settings.AppSettingsState; +import org.testshift.testcube.settings.AskJavaPathDialogWrapper; +import org.testshift.testcube.settings.AskMavenHomeDialogWrapper; + +import javax.swing.*; +import java.awt.*; +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +public class CFGWindow extends JPanel implements Disposable { + private static final Logger logger = Logger.getInstance(CFGWindow.class); + + private Project project; + private String targetClass; + private String targetMethod; + private static JPanel contentPanel; + private CFGPanel cfgPanel; + private JPanel buttonPanel; + private JButton finish; + private JButton close; + private String moduleRootPath; + private String testClass; + private String testMethod; + private Set initialCoveredLines; + private Set initialCoveredBranches; + + private int branchNum; + + public CFGWindow(Project project, String targetClass, String targetMethod, String source, + Set initialCoveredLines, + Set initialCoverdBranches, + String moduleRootPath, String testClass, String testMethod, int branchNum){ + this.project = project; + this.targetClass = targetClass; + this.targetMethod = targetMethod; + this.moduleRootPath = moduleRootPath; + this.testClass = testClass; + this.testMethod = testMethod; + this.initialCoveredLines = initialCoveredLines; + this.initialCoveredBranches = initialCoverdBranches; + this.branchNum = branchNum; + + this.contentPanel = new JPanel(); + this.buttonPanel = new JPanel(); + this.finish = new JButton("Ok"); + this.close = new JButton("Close"); +// this.cfgPanel = cfgPanel; +// this.buttonPanel = buttonPanel; + ImageFormat imageFormat = ImageFormat.PNG; + int page = 0; + int version = 0; + RenderCommand.Reason reason = RenderCommand.Reason.FILE_SWITCHED; + this.cfgPanel = new CFGPanel(/*sourceFilePath,*/ source, imageFormat, page, version, initialCoveredLines, initialCoveredBranches); + cfgPanel.render(reason); + cfgPanel.displayResult(reason); + cfgPanel.maintainInitialCover(); + cfgPanel.setLayout(new GridLayout()); + finish.addActionListener(l->finishSelection(project)); + close.addActionListener(l->cancel()); + buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); + buttonPanel.add(finish); + buttonPanel.add(close); + contentPanel.setVisible(true); + contentPanel.setLayout(new BorderLayout()); + contentPanel.add(cfgPanel, BorderLayout.CENTER); + contentPanel.add(buttonPanel, BorderLayout.SOUTH); +// contentPanel.setVisible(true); + } + + private void cancel() { + dispose(); + } + + + public static JPanel getContent() { + return contentPanel; + } + + public String getDisplayName(){ + return "Control Flow Graph of "+ targetMethod; + } + + private void finishSelection(Project project) { + if(branchNum>0) { + cfgPanel.recordHilight(); + String selectedBranch = cfgPanel.getHilightText(); + if (!selectedBranch.equals("")) { + try { + runDSpot(project, selectedBranch); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } +// TestCubeNotifier notifier = new TestCubeNotifier(); +// notifier.notify(project, +// "Test Cube found " + 1 + " amplified test cases.", +// +// new InspectResultWithCFGAction(project, testClass, testMethod, +// new CFGPanel(cfgPanel), targetMethod), +// new InspectDSpotTerminalOutputAction()); + } + else { + NoSelectionDialog dialog = new NoSelectionDialog(); + dialog.pack();; + dialog.setVisible(true); + } + } + else{ + String selectedBranch = "noBranch"; + try { + runDSpot(project, selectedBranch); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + dispose(); + } + + public void runDSpot(Project currentProject, String branch) throws IOException, InterruptedException { + + IdeaPluginDescriptor testCubePlugin = PluginManagerCore.getPlugin(PluginId.getId("org.testshift.testcube")); + if (testCubePlugin != null) { + testCubePlugin.getPluginClassLoader(); + } + + Sdk projectSdk = ProjectRootManager.getInstance(currentProject).getProjectSdk(); + + if (projectSdk != null) { + AppSettingsState.getInstance().java8Path = projectSdk.getHomePath(); + } + + if (AppSettingsState.getInstance().java8Path.isEmpty()) { + AskJavaPathDialogWrapper dialog = new AskJavaPathDialogWrapper(); + dialog.showAndGet(); + boolean pathValid = dialog.setJavaPathIfValid(); + // todo handle non valid + } + + // check if Gradle or Maven + boolean isGradle = ExternalSystemApiUtil.isExternalSystemAwareModule(new ProjectSystemId("GRADLE"), + ModuleManager.getInstance(currentProject) + .getModules()[0]); + boolean isMaven = ExternalSystemApiUtil.isExternalSystemAwareModule(new ProjectSystemId("Maven"), + ModuleManager.getInstance(currentProject) + .getModules()[0]); + + String relativePathToClasses = ""; + String relativePathToTestClasses = ""; + String automaticBuilder = ""; + if (isMaven) { + relativePathToClasses = "target" + File.separator + "classes" + File.separator; + relativePathToTestClasses = "target" + File.separator + "test-classes" + File.separator; + automaticBuilder = "Maven"; + } else { + relativePathToClasses = "bin" + File.separator + "main" + File.separator; + relativePathToTestClasses = "bin" + File.separator + "test" + File.separator; + automaticBuilder = "Gradle"; + if (!isGradle) { + logger.info("Neither Gradle nor Maven"); + } + } + + if (isMaven && AppSettingsState.getInstance().mavenHome.isEmpty()) { + AskMavenHomeDialogWrapper dialog = new AskMavenHomeDialogWrapper(); + dialog.showAndGet(); + boolean mavenHomeValid = dialog.setMavenHomeIfValid(); + // todo handle non valid + } + + String javaHome = AppSettingsState.getInstance().java8Path; + String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; + + String pluginPath = PathManager.getPluginsPath(); + String dSpotPath = pluginPath + File.separator + "test-cube" + File.separator + "lib" + File.separator + + "dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar"; + + String finalRelativePathToClasses = relativePathToClasses; + String finalRelativePathToTestClasses = relativePathToTestClasses; + String finalAutomaticBuilder = automaticBuilder; + Task.Backgroundable dspotTask = new Task.Backgroundable(currentProject, "Amplifying test", true) { + + public void run(@NotNull ProgressIndicator indicator) { + // clean output directory + // todo close open amplification result windows or split output into different directories + try { + File outputDirectory = new File(Util.getDSpotOutputPath(currentProject)); + if (outputDirectory.exists()) { + FileUtils.cleanDirectory(outputDirectory); + } + } catch (IOException e) { + e.printStackTrace(); + } + + String targetModule = ""; + if (isMaven) { + targetModule = moduleRootPath.replace(currentProject.getBasePath() + "/", ""); + } + + // @formatter:off + List dSpotStarter = new ArrayList<>(Arrays.asList(javaBin, "-jar", dSpotPath, + "--absolute-path-to-project-root", currentProject.getBasePath(), + "--relative-path-to-classes", finalRelativePathToClasses, + "--relative-path-to-test-classes", finalRelativePathToTestClasses, + "--test-criterion", "BranchCoverageSelector", + "--input-ampl-distributor", "RandomInputAmplDistributor", + "--test", testClass, + // TODO handlle null on testMethod + "--test-cases", testMethod, + "--target-class", targetClass, + "--target-method", targetMethod, + "--target-branch", branch, + "--output-directory", Util.getDSpotOutputPath(currentProject), + "--amplifiers", "TargetMethodAdderOnExistingObjectsAmplifier", + "--max-test-amplified", "25", + "--automatic-builder", finalAutomaticBuilder, + //"--generate-new-test-class", + //"--keep-original-test-methods", + "--verbose", + "--dev-friendly", + "--clean", + "--with-comment=None")); + // @formatter:on + +// if (!AppSettingsState.getInstance().generateAssertions) { +// dSpotStarter.add("--only-input-amplification"); +// } + @NotNull Module[] modules = ModuleManager.getInstance(currentProject).getModules(); + if (modules.length > 1) { + dSpotStarter.add("--target-module"); + dSpotStarter.add(targetModule); + } + + ProcessBuilder pb = new ProcessBuilder(dSpotStarter); + + pb.environment().put("MAVEN_HOME", AppSettingsState.getInstance().mavenHome); + + File workdir = new File(Util.getTestCubeOutputPath(currentProject) + File.separator + "workdir"); + if (!workdir.exists()) { + if (!workdir.mkdirs()) { + logger.error("Could not create workdir output directory!"); + } + } + File workdirTarget = new File(workdir.getPath() + File.separator + "target" + File.separator + "dspot"); + if (!workdirTarget.exists()) { + if (!workdirTarget.mkdirs()) { + logger.error("Could not create workdir/target/dspot output directory!"); + } + } + pb.directory(workdir); + + pb.redirectErrorStream(true); + try { + Process p = pb.start(); + + File outputDir = new File(Util.getTestCubeOutputPath(currentProject)); + if (!outputDir.exists()) { + if (!outputDir.mkdirs()) { + logger.error("Could not create Test Cube output directory!"); + } + } + + File dSpotTerminalOutput = new File( + Util.getTestCubeOutputPath(currentProject) + File.separator + "terminal_output_dspot.txt"); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(dSpotTerminalOutput))) { + InputStream is = p.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + for (String line = br.readLine(); line != null; line = br.readLine()) { + System.out.println(line); + writer.write(line); + writer.newLine(); + } + } + p.waitFor(); + System.out.println(p.exitValue()); + + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + } + + Util.sleepAndRefreshProject(currentProject); + + TestCubeNotifier notifier = new TestCubeNotifier(); +// TestClassJSON result = Util.getResultJSON(currentProject, testClass); + TestClassBranchCoverageJSON coverageResult = + (TestClassBranchCoverageJSON) Util.getBranchCoverageJSON(project, testClass); + if (coverageResult.getTestCases() == null || coverageResult == null) { + notifier.notify(currentProject, "No new test cases found.", + new InspectDSpotTerminalOutputAction()); + } else { + int amplifiedTestCasesCount = coverageResult.getTestCases().size(); + + if (amplifiedTestCasesCount == 0) { + notifier.notify(currentProject, "Could find no new test cases through amplification."); + } else { + notifier.notify(currentProject, + "Test Cube found " + amplifiedTestCasesCount + " amplified test cases.", + // TODO: create a new Action for CFG + new InspectResultWithCFGAction(currentProject, testClass, testMethod, + new CFGPanel(cfgPanel), targetMethod), + new InspectDSpotTerminalOutputAction()); + } + } + } + }; + + BackgroundableProcessIndicator processIndicator = new BackgroundableProcessIndicator(dspotTask); + ProgressManager.getInstance().runProcessWithProgressAsynchronously(dspotTask, processIndicator); + } + + private void close(Project project) { + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Test Cube"); + if (toolWindow != null) { + toolWindow.getContentManager() + .removeContent(toolWindow.getContentManager().findContent(getDisplayName()), true); + if (toolWindow.getContentManager().getContentCount() == 0) { + toolWindow.hide(); + } + } + } + + @Override + public void dispose() { + buttonPanel.removeAll(); + cfgPanel.dispose(); + for(Component component: contentPanel.getComponents()){ + if (component instanceof Disposable) { + Disposer.dispose((Disposable) component); + } + } + close(project); + } +} diff --git a/src/main/java/org/testshift/testcube/branches/CodeToUml.java b/src/main/java/org/testshift/testcube/branches/CodeToUml.java new file mode 100644 index 0000000..3b195bb --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/CodeToUml.java @@ -0,0 +1,420 @@ +package org.testshift.testcube.branches; + +import org.apache.commons.collections.map.HashedMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CodeToUml { + + + + public static Map codeToUml(String code, int startLineNumber){ + int branchNum = 0; + String[] lines = code.split("\n"); +// int[] instrumented = new int[lines.length]; + branchNum += instrumentIF(lines, startLineNumber); + branchNum += instrumentDoWhile(lines, startLineNumber); + branchNum += instrumentWhile(lines, startLineNumber); + branchNum += instrumentSwitch(lines, startLineNumber); + branchNum += instrumentFor(lines, startLineNumber); + branchNum += instrumentTriple(lines, startLineNumber); + instrumentNormal(lines, startLineNumber); + + + String output = "@startuml\n" + + "\n" + + "start\n"; + + for(String line: lines){ + output = output+line+"\n"; + } + + output = output + "stop\n" + + "\n" + + "@enduml"; + + String[] instrumentedLines = output.split("\n"); + String result = ""; + for(String line : instrumentedLines){ + if(!nonline(line)){ +// line = line.replaceAll("\\}", ""); +// line = line.replaceAll("\\{", ""); + result = result + line +"\n"; + } + } + return Map.of(result, branchNum); + } + + public static class Block{ + private String identifier; + private int branchLine; + private int endLine; + private char endMark; + + Block(String identifier, int branchLine, int endLine, char endMark){ + this.identifier = identifier; + this.branchLine = branchLine; + this.endLine = endLine; + this.endMark = endMark; + } + + public String getIdentifier() { + return identifier; + } + + public int getBranchLine() { + return branchLine; + } + + public int getEndLine() { + return endLine; + } + + public char getEndMark() { + return endMark; + } + } + + public static int instrumentWhile(String[] lines, int startLineNumber){ + int branchNum = 0; + for(int i=0; i str.contains("for")).findAny().isEmpty()){ + branchNum += 1; + int endBracket = findEndBracket(lines, i, lines[i].indexOf("for")+3); + char endMark = findEndMark(lines, endBracket+1, i); + int endLine = findEndline(endMark, lines, i); + StringBuilder stringBuilder = new StringBuilder(lines[i]); + stringBuilder.insert(endBracket+1, "is ("+(starLineNumber+i)+": True / enter loop)\n"); + lines[i] = stringBuilder.toString(); + lines[i] = lines[i].replace("for", "\nwhile"); + stringBuilder = new StringBuilder(lines[endLine]); + stringBuilder.insert(lines[endLine].indexOf(endMark)+1, "\nendwhile ("+(i+starLineNumber)+": False " + + "/ end loop)\n"); + lines[endLine] = stringBuilder.toString(); + } + } + return branchNum; + } + + public static int instrumentTriple(String[] lines, int startLineNumber){ + int branchNum = 0; + for(int i=0; i blocks = new Stack<>(); + int branchNum = 0; + //process lines + for (int i = 0; i < lines.length; i++) { + lines[i] = lines[i].strip().replaceAll("\\/\\/[^\\n]*", "\n"); + if (lines[i].contains("if")) { +// instrumented[i] = 1; + branchNum+=1; + char endMark = ' '; + int endLine=-1; + // add then(true) to the bracket + int index = lines[i].indexOf("if"); + int endBracket = findEndBracket(lines, i, index+2); + if(endBracket!=-1){ + endMark = findEndMark(lines, endBracket+1, i); + // and then (true) and start a new line for following content + StringBuilder stringBuilder = new StringBuilder(lines[i]); + stringBuilder.insert(endBracket+1, + "then ("+ (startLineNumber+i)+": True)\n:"+(startLineNumber+i)+": "); + lines[i] = stringBuilder.toString(); + } + endLine = findEndline(endMark, lines, i); + + // record if block + if(lines[i].contains("else if")){ + // we need every else to identify branch + int branchline = blocks.peek().getBranchLine(); + lines[i] = lines[i].replaceFirst("else", "\nelse ("+ (branchline+startLineNumber) +": False)\n"); + blocks.push(new Block("elseif", i, endLine, endMark)); + } + else { + //encounter new if, add end if to previous if and delete related blobks in stack + if(!blocks.isEmpty() && blocks.peek().getEndLine()<=i) { + Block lastEndBlock = blocks.pop(); + int lastEndLine = lastEndBlock.getEndLine(); + StringBuilder stringBuilder = new StringBuilder(lines[lastEndLine]); + int endColumn = lines[lastEndLine].substring(lines[lastEndLine].indexOf('{')+1).indexOf(lastEndBlock.getEndMark()) + lines[lastEndLine].indexOf('{')+1; + if(lastEndBlock.getIdentifier().equals("else")) { + stringBuilder.insert(endColumn + 1, "\nendif\n"); + } + else{ + int branchline = lastEndBlock.getBranchLine(); + stringBuilder.insert(endColumn + 1, "\nelse ("+ (branchline+startLineNumber) +": False)" + + "\nendif\n"); + blocks.push(lastEndBlock); + } +// lines[lastEndBlock.getEndLine()] = stringBuilder.toString(); + while (!blocks.isEmpty() && !(blocks.peek().getIdentifier().equals("if"))) { + blocks.pop(); + stringBuilder.insert(stringBuilder.lastIndexOf("endif\n")+6, "endif\n"); + } + blocks.pop(); + lines[lastEndBlock.getEndLine()] = stringBuilder.toString(); + } + lines[i] = lines[i].replace("if", "\nif"); + blocks.push(new Block("if", i, endLine, endMark)); + } + } else if (lines[i].contains("else")) { +// instrumented[i] =1; + char endMark = findEndMark(lines, lines[i].indexOf("else")+4, i); + int endLine = findEndline('}', lines, i); + int branchline = blocks.peek().getBranchLine(); + lines[i] = lines[i].replaceFirst("else", + "\nelse ("+ (branchline+startLineNumber) +": False)\n:"+(startLineNumber+branchline)+": "); + blocks.push(new Block("else", i, endLine, endMark)); + } + } + + while(!blocks.isEmpty()){ + Block lastEndBlock = blocks.pop(); + int lastEndLine = lastEndBlock.getEndLine(); + StringBuilder stringBuilder = new StringBuilder(lines[lastEndLine]); + int endColumn = lines[lastEndLine].substring(lines[lastEndLine].indexOf('{')+1).indexOf(lastEndBlock.getEndMark()) + lines[lastEndLine].indexOf('{')+1; + if(lastEndBlock.getIdentifier().equals("else")) { + stringBuilder.insert(endColumn + 1, "\nendif\n"); + } + else{ + int branchline = lastEndBlock.getBranchLine(); + stringBuilder.insert(endColumn + 1, "\nelse ("+ (branchline+startLineNumber) +": False)\nendif\n"); + blocks.push(lastEndBlock); + } +// lines[lastEndBlock.getEndLine()] = stringBuilder.toString(); + while (!blocks.isEmpty() && !(blocks.peek().getIdentifier().equals("if"))) { + blocks.pop(); + stringBuilder.insert(stringBuilder.lastIndexOf("endif\n")+6, "\nendif\n"); + } + blocks.pop(); + lines[lastEndBlock.getEndLine()] = stringBuilder.toString(); + } + return branchNum; + } + + private static char findEndMark(String[] lines, int startIndex, int startLine){ + for(int i = startIndex; i brackets = new Stack<>(); + for(int start = startIndex; start braces = new Stack<>(); + int index = lines[lineNumber].indexOf("{"); + braces.push('}'); + for(int start = index+1; start Text(String line){ + Pattern p1=Pattern.compile("\"(.*?)\""); + + Matcher m = p1.matcher(line); + + ArrayList list = new ArrayList(); + while (m.find()) { + list.add(m.group().trim().replace("\"","")+" "); + } + return list; + } + + private static boolean nonline(String line){ + if(line.equals("")){ + return true; + } + if(line.charAt(0) == ':'){ + String[] tmps = line.split(":"); + if(tmps[tmps.length-1].replaceAll("\\{", "").replaceAll("\\}", "").replaceAll(":", "").replaceAll(";","").replaceAll("\n", "").strip().equals("")){ + return true; + } + } + else{ + if(line.replaceAll("\\{", "").replaceAll("\\}", "").replaceAll(":", "").replaceAll(";","").replaceAll("\n", "").strip().equals("")){ + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/testshift/testcube/branches/CreateTestDialog.form b/src/main/java/org/testshift/testcube/branches/CreateTestDialog.form new file mode 100644 index 0000000..06c45a5 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/CreateTestDialog.form @@ -0,0 +1,64 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/testshift/testcube/branches/CreateTestDialog.java b/src/main/java/org/testshift/testcube/branches/CreateTestDialog.java new file mode 100644 index 0000000..a6a5928 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/CreateTestDialog.java @@ -0,0 +1,43 @@ +package org.testshift.testcube.branches; + +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.testIntegration.LanguageTestCreators; +import com.intellij.testIntegration.TestCreator; + +import javax.swing.*; +import java.awt.event.*; + +public class CreateTestDialog extends JDialog { + private JPanel contentPane; + private JButton buttonOK; + private JLabel Content; + private Editor editor; + private PsiFile file; + + public CreateTestDialog(Editor editor, PsiFile file) { + setContentPane(contentPane); + setLocationRelativeTo(null); + setModal(false); + this.editor = editor; + this.file = file; + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(e); + } + }); + } + + private void onOK(ActionEvent e) { + // add your code here + dispose(); + for (TestCreator creator : LanguageTestCreators.INSTANCE.allForLanguage(file.getLanguage())) { + if (!creator.isAvailable(file.getProject(), editor, file)) continue; + creator.createTest(file.getProject(), editor,file); + } + } +} diff --git a/src/main/java/org/testshift/testcube/branches/NoBranchDialog.java b/src/main/java/org/testshift/testcube/branches/NoBranchDialog.java index 1c04ece..09c493a 100644 --- a/src/main/java/org/testshift/testcube/branches/NoBranchDialog.java +++ b/src/main/java/org/testshift/testcube/branches/NoBranchDialog.java @@ -43,7 +43,6 @@ public void actionPerformed(ActionEvent e) { } private void onOK() { - // TODO: runDspot and show the result dispose(); } diff --git a/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.form b/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.form new file mode 100644 index 0000000..ca833f9 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.form @@ -0,0 +1,64 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.java b/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.java new file mode 100644 index 0000000..4b9fc7d --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/NoSelectionDialog.java @@ -0,0 +1,60 @@ +package org.testshift.testcube.branches; + +import javax.swing.*; +import java.awt.event.*; + +public class NoSelectionDialog extends JDialog { + private JPanel contentPane; + private JButton buttonOK; +// private JButton buttonCancel; + private JLabel Content; + + public NoSelectionDialog() { + setContentPane(contentPane); + setLocationRelativeTo(null); + setModal(false); + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + +// buttonCancel.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// onCancel(); +// } +// }); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + + private void onOK() { + dispose(); + } + + private void onCancel() { + // add your code here if necessary + dispose(); + } + + public static void main(String[] args) { + NoBranchDialog dialog = new NoBranchDialog(); + dialog.pack(); + dialog.setVisible(true); + dialog.setSize(800,400); + } +} diff --git a/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.form b/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.form new file mode 100644 index 0000000..b7670f5 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.form @@ -0,0 +1,64 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.java b/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.java new file mode 100644 index 0000000..28dd0e3 --- /dev/null +++ b/src/main/java/org/testshift/testcube/branches/WriteObjectDialog.java @@ -0,0 +1,36 @@ +package org.testshift.testcube.branches; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class WriteObjectDialog extends JDialog { + private JPanel contentPane; + private JButton buttonOK; + private JPanel ContentPane; + + public WriteObjectDialog() { + setContentPane(contentPane); + setLocationRelativeTo(null); + setModal(false); + + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + } + + private void onOK() { + // add your code here + dispose(); + } + + public static void main(String[] args) { + WriteObjectDialog dialog = new WriteObjectDialog(); + dialog.pack(); + dialog.setVisible(true); + System.exit(0); + } +} diff --git a/src/main/java/org/testshift/testcube/branches/preview/image/ImageContainerPng.java b/src/main/java/org/testshift/testcube/branches/preview/image/ImageContainerPng.java index 136aa05..e659a9b 100644 --- a/src/main/java/org/testshift/testcube/branches/preview/image/ImageContainerPng.java +++ b/src/main/java/org/testshift/testcube/branches/preview/image/ImageContainerPng.java @@ -13,10 +13,12 @@ import org.testshift.testcube.branches.rendering.RenderRequest; import org.testshift.testcube.branches.rendering.RenderResult; import org.testshift.testcube.branches.rendering.Zoom; +import org.testshift.testcube.misc.Util; import javax.swing.*; import java.awt.*; import java.util.List; +import java.util.Set; public class ImageContainerPng extends JLabel{ private RenderResult renderResult; @@ -88,7 +90,7 @@ public static void initLinks( @NotNull ImageItem imageItem, RenderRequest render // } // long start = System.currentTimeMillis(); // LinkNavigator navigator = new LinkNavigator(renderRequest, renderResult, project); - boolean showUrlLinksBorder = false; +// boolean showUrlLinksBorder = false; Zoom zoom = renderRequest.getZoom(); image.removeAll(); @@ -97,10 +99,13 @@ public static void initLinks( @NotNull ImageItem imageItem, RenderRequest render Rectangle area = getRectangle(zoom, linkData); - JLabel button = new MyJLabel(linkData, area, showUrlLinksBorder); + MyJLabel button = new MyJLabel(linkData, area/*, showUrlLinksBorder*/); //When user clicks on item, url is opened in default system browser - button.addMouseListener(new MyMouseAdapter(linkData, renderRequest)); + if(linkData.getText().contains("True") || linkData.getText().contains("False")) { + button.setIsbranch(true); + button.addMouseListener(new MyMouseAdapter(linkData, renderRequest)); + } image.add(button); } @@ -127,11 +132,62 @@ public void highlight(String text) { Component[] components = this.getComponents(); for (Component component : components) { MyJLabel jLabel = (MyJLabel) component; - if(jLabel.getLinkData().getText().contains(text)) { + if(jLabel.getLinkData().getText().equals(text)) { + jLabel.highlight(text); + } + else if(jLabel.isHighlighted()){ jLabel.highlight(text); - break; } } } + public void coverLines(Set lines) { + Component[] components = this.getComponents(); + for (Component component : components) { + MyJLabel jLabel = (MyJLabel) component; + if(!jLabel.isBranch()) { + jLabel.coverLines(lines); + } + } + } + + public void coverBranches(Set branches) { + Component[] components = this.getComponents(); + for (Component component : components) { + MyJLabel jLabel = (MyJLabel) component; + if(jLabel.isBranch()) { + jLabel.coverBranches(branches); + } + } + } + + public void coverNewLines(Set lines) { + Component[] components = this.getComponents(); + for (Component component : components) { + MyJLabel jLabel = (MyJLabel) component; + if(!jLabel.isBranch() && !jLabel.isCovered()) { + jLabel.coverNewLines(lines); + } + } + } + + public void coverNewBranches(Set branches) { + Component[] components = this.getComponents(); + for (Component component : components) { + MyJLabel jLabel = (MyJLabel) component; + if(jLabel.isBranch() && !jLabel.isCovered()) { + jLabel.coverNewBranches(branches); + } + } + } + + public void removeNewCover() { + Component[] components = this.getComponents(); + for (Component component : components) { + MyJLabel jLabel = (MyJLabel) component; + if(jLabel.isNewCovered()) { + jLabel.unNewCover(); + } + } + } } diff --git a/src/main/java/org/testshift/testcube/branches/preview/image/links/Highlighter.java b/src/main/java/org/testshift/testcube/branches/preview/image/links/Highlighter.java index 2e795d7..a518fec 100644 --- a/src/main/java/org/testshift/testcube/branches/preview/image/links/Highlighter.java +++ b/src/main/java/org/testshift/testcube/branches/preview/image/links/Highlighter.java @@ -3,47 +3,34 @@ import com.intellij.util.Alarm; import org.testshift.testcube.branches.CFGPanel; import org.testshift.testcube.branches.preview.image.ImageContainerPng; +import org.testshift.testcube.misc.Util; //import org.testshift.testcube.branches.preview.image.ImageContainerSvg; import javax.swing.*; import java.awt.*; import java.util.Collections; +import java.util.LinkedList; import java.util.List; +import java.util.Set; public class Highlighter { private Alarm myAlarm; +// private String highlightText; public Highlighter() { myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); +// String highlightText=""; // plantUmlSettings = PlantUmlSettings.getInstance(); } public void highlightImages(JPanel panel, String text) { -//// if (editor == null || editor.getProject() == null || !previewPanel.isPreviewVisible()) { -//// return; -//// } -// myAlarm.cancelAllRequests(); -// myAlarm.addRequest(() -> { -// if (editor.isDisposed()) { -// return; -// } highlight(panel, text); -// }, 10); } private void highlight(JPanel panel, String text) { -// long start = System.currentTimeMillis(); -// JPanel imagesPanel = panel.getImagesPanel(); Component[] components = panel.getComponents(); // if (components.length > 0) { -// List list; -//// if (plantUmlSettings.isHighlightInImages()) { -// list = getListForHighlighting(editor); -//// } else { -// list = Collections.emptyList(); -//// } -// for (Component component : components) { if (component instanceof ImageContainerPng) { ImageContainerPng imageContainer = (ImageContainerPng) component; @@ -53,4 +40,37 @@ private void highlight(JPanel panel, String text) { } //// LOG.debug("highlightImages done in ", System.currentTimeMillis() - start, "ms"); } + + public void coverInitialLinesAndBranches(JPanel panel, Set lines, Set branches){ + Component[] components = panel.getComponents(); + + if (components.length > 0) { + for (Component component : components) { + if (component instanceof ImageContainerPng) { + ImageContainerPng imageContainer = (ImageContainerPng) component; + imageContainer.coverLines(lines); + imageContainer.coverBranches(branches); + } + } + } + } + + public void coverNewLinesAndBranches(JPanel panel, Set newCoveredLines, + Set newCoveredBranches) { + Component[] components = panel.getComponents(); + if (components.length > 0) { + for (Component component : components) { + if (component instanceof ImageContainerPng) { + ImageContainerPng imageContainer = (ImageContainerPng) component; + imageContainer.removeNewCover(); + imageContainer.coverNewLines(newCoveredLines); + imageContainer.coverNewBranches(newCoveredBranches); + } + } + } + } + +// public String getHighlightText() { +// return highlightText; +// } } diff --git a/src/main/java/org/testshift/testcube/branches/preview/image/links/MyJLabel.java b/src/main/java/org/testshift/testcube/branches/preview/image/links/MyJLabel.java index 5a7749e..5774eee 100644 --- a/src/main/java/org/testshift/testcube/branches/preview/image/links/MyJLabel.java +++ b/src/main/java/org/testshift/testcube/branches/preview/image/links/MyJLabel.java @@ -2,31 +2,43 @@ import com.intellij.ui.ColoredSideBorder; import org.jetbrains.annotations.NotNull; +import org.junit.Test; import org.testshift.testcube.branches.rendering.ImageItem; +import org.testshift.testcube.misc.Util; import javax.swing.*; import javax.swing.border.CompoundBorder; import javax.swing.border.LineBorder; import java.awt.*; import java.util.List; +import java.util.Set; public class MyJLabel extends JLabel { - private static final ColoredSideBorder BORDER = new ColoredSideBorder(Color.RED, Color.RED, Color.RED, Color.RED, 1); + private static final ColoredSideBorder BORDER = new ColoredSideBorder(Color.RED, Color.RED, Color.RED, Color.RED, + 2); + private static final CompoundBorder COVERBORDER = getCoverBorder(); private static final CompoundBorder HIGHLIGHT = getCompoundBorder(); + private static final CompoundBorder NEWCOVERBORDER = getNewCoverBorder(); private final ImageItem.LinkData linkData; private Rectangle area; - private final boolean showUrlLinksBorder; +// private final boolean showUrlLinksBorder; private boolean highlighted; + private boolean covered; + private boolean newCovered; + private boolean isbranch; - public MyJLabel(ImageItem.LinkData linkData, Rectangle area, boolean showUrlLinksBorder) { + public MyJLabel(ImageItem.LinkData linkData, Rectangle area/*, boolean showUrlLinksBorder*/) { this.linkData = linkData; this.area = area; - this.showUrlLinksBorder = showUrlLinksBorder; +// this.showUrlLinksBorder = showUrlLinksBorder; this.highlighted=false; - if (showUrlLinksBorder) { - setBorder(BORDER); - } + this.covered = false; + this.newCovered = false; + this.isbranch = false; +// if (showUrlLinksBorder) { +// setBorder(BORDER); +// } setLocation(area.getLocation()); setSize(area.getSize()); @@ -40,9 +52,27 @@ public ImageItem.LinkData getLinkData() { @NotNull private static CompoundBorder getCompoundBorder() { - LineBorder lineBorder1 = new LineBorder(Color.BLACK, 1); - LineBorder lineBorder2 = new LineBorder(Color.GREEN, 1); - LineBorder lineBorder3 = new LineBorder(Color.BLACK, 1); + LineBorder lineBorder1 = new LineBorder(Color.RED, 1); + LineBorder lineBorder2 = new LineBorder(Color.RED, 1); + LineBorder lineBorder3 = new LineBorder(Color.RED, 1); + CompoundBorder border = new CompoundBorder(lineBorder1, new CompoundBorder(lineBorder2, lineBorder3)); + return border; + } + + @NotNull + private static CompoundBorder getCoverBorder() { + LineBorder lineBorder1 = new LineBorder(new Color(0,153,0), 1); + LineBorder lineBorder2 = new LineBorder(new Color(0,153,0), 1); //Dark GREEN + LineBorder lineBorder3 = new LineBorder(new Color(0,153,0), 1); + CompoundBorder border = new CompoundBorder(lineBorder1, new CompoundBorder(lineBorder2, lineBorder3)); + return border; + } + + @NotNull + private static CompoundBorder getNewCoverBorder() { + LineBorder lineBorder1 = new LineBorder(new Color(0,255,51), 1); + LineBorder lineBorder2 = new LineBorder(new Color(0,255,51), 1); //Light GREEN + LineBorder lineBorder3 = new LineBorder(new Color(0,255,51), 1); CompoundBorder border = new CompoundBorder(lineBorder1, new CompoundBorder(lineBorder2, lineBorder3)); return border; } @@ -58,35 +88,125 @@ private static CompoundBorder getCompoundBorder() { // } public void highlight(String text) { - if (!highlighted) { + if (this.linkData.getText().equals(text) && !highlighted && !covered && !newCovered) { highlight(); highlighted = true; } - else{ + else if(highlighted){ unhighlight(); - highlighted =false; + highlighted = false; } } private void unhighlight() { - //BORDER可以直接不要 - setBorder(showUrlLinksBorder ? BORDER : null); + setBorder(null); setLocation(area.getLocation()); setSize(area.getSize()); } private void highlight() { -// setBackground(Color.RED); setBorder(HIGHLIGHT); Rectangle rectangle = new Rectangle(area.x - 2, area.y - 2, area.width + 4, area.height + 4); setLocation(rectangle.getLocation()); setSize(rectangle.getSize()); } + public void coverLines(Set lines){ + boolean contain = containsLine(linkData.getText(), lines); + if (contain) { + if (!covered) { + cover(); + covered = true; + } + } + } + + public void coverBranches(Set branchs){ + boolean contain = containsBranch(linkData.getText(), branchs); + if (contain) { + if (!covered) { + cover(); + covered = true; + } + } + } + + + public void coverNewLines(Set lines) { + boolean contain = containsLine(linkData.getText(), lines); + if (contain) { + newCover(); + newCovered = true; + } + } + + public void coverNewBranches(Set branchs){ + boolean contain = containsBranch(linkData.getText(), branchs); + if (contain) { + newCover(); + newCovered = true; + } + } + + private void cover(){ + setBorder(COVERBORDER); + setLocation(area.getLocation()); + setSize(area.getSize()); + } + + private void newCover(){ + setBorder(NEWCOVERBORDER); + setLocation(area.getLocation()); + setSize(area.getSize()); + } + + public void unNewCover(){ + setBorder(null); + setLocation(area.getLocation()); + setSize(area.getSize()); + newCovered = false; + } + + private boolean containsLine(String text, Set lines) { + for (String line : lines) { + if (line.length() == 0) { + continue; + } + if (text.contains(line)) { + return true; + } + } + return false; + } + + private boolean containsBranch(String text, Set branchs) { + for (Util.Branch branch : branchs) { + if (text.contains(branch.getLine()) && text.contains(branch.getSymbol())) { + return true; + } + } + return false; + } + public boolean isHighlighted(){ return highlighted; } + public boolean isBranch() { + return isbranch; + } + + public boolean isCovered() { + return covered; + } + + public boolean isNewCovered() { + return newCovered; + } + + public void setIsbranch(boolean isbranch) { + this.isbranch = isbranch; + } public String getLinkDataText(){ return linkData.getText(); diff --git a/src/main/java/org/testshift/testcube/branches/rendering/DiagramFactory.java b/src/main/java/org/testshift/testcube/branches/rendering/DiagramFactory.java index 0da2190..5ac2971 100644 --- a/src/main/java/org/testshift/testcube/branches/rendering/DiagramFactory.java +++ b/src/main/java/org/testshift/testcube/branches/rendering/DiagramFactory.java @@ -107,7 +107,8 @@ public ImageItem generateImageItem(RenderRequest renderRequest, String documentS if (wrongResultFormat) { resultFormat = ImageFormat.PNG; } - return new ImageItem(renderRequest.getBaseDir(), resultFormat, documentSource, pageSource, page, description, bytes, svgBytes, renderingType, getTitle(page), getFilename(page), null); + return new ImageItem(/*renderRequest.getBaseDir(),*/ resultFormat, documentSource, pageSource, page, description + , bytes, svgBytes, renderingType, getTitle(page), getFilename(page), null); } public DiagramDescription outputImage(OutputStream imageStream, int numImage, FileFormatOption formatOption) { diff --git a/src/main/java/org/testshift/testcube/branches/rendering/ImageItem.java b/src/main/java/org/testshift/testcube/branches/rendering/ImageItem.java index e390524..ae59283 100644 --- a/src/main/java/org/testshift/testcube/branches/rendering/ImageItem.java +++ b/src/main/java/org/testshift/testcube/branches/rendering/ImageItem.java @@ -49,7 +49,7 @@ public class ImageItem { private final Map componentMap = new HashMap<>(); private BufferedImage bufferedImage; - public ImageItem(@Nullable File baseDir, + public ImageItem(//@Nullable File baseDir, @NotNull ImageFormat format, @NotNull String documentSource, @Nullable String pageSource, @@ -72,7 +72,7 @@ public ImageItem(@Nullable File baseDir, this.imageBytes = imageBytes; this.exception = exception; - this.links = this.parseLinks(svgBytes, baseDir); + this.links = this.parseLinks(svgBytes/*, baseDir*/); } public ImageItem(int page, ImageItem item, @NotNull ImageFormat format) { @@ -143,7 +143,7 @@ public static BufferedImage getBufferedImage(@NotNull byte[] imageBytes) throws public String getTitle() { return title; } - private List parseLinks(byte[] svgData, File baseDir) { + private List parseLinks(byte[] svgData/*, File baseDir*/) { if (svgData == null || svgData.length == 0) { return Collections.emptyList(); } diff --git a/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlNormalRenderer.java b/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlNormalRenderer.java index b1ba830..dc60cad 100644 --- a/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlNormalRenderer.java +++ b/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlNormalRenderer.java @@ -56,7 +56,8 @@ private void normalRendering(RenderRequest renderRequest, String[] sourceSplit, renderResult.addRenderedImage(imageItem); } else { // logger.debug("page ", page, " title only"); - ImageItem imageItem = new ImageItem(renderRequest.getBaseDir(), renderRequest.getFormat(), documentSource, pageSource, page, RenderResult.TITLE_ONLY, null, null, RenderingType.NORMAL, factory.getTitle(page), factory.getFilename(page), null); + ImageItem imageItem = new ImageItem(/*renderRequest.getBaseDir(),*/ renderRequest.getFormat(), + documentSource, pageSource, page, RenderResult.TITLE_ONLY, null, null, RenderingType.NORMAL, factory.getTitle(page), factory.getFilename(page), null); renderResult.addUpdatedTitle(imageItem); } } diff --git a/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlRendererUtil.java b/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlRendererUtil.java index 3494b62..c5d697c 100644 --- a/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlRendererUtil.java +++ b/src/main/java/org/testshift/testcube/branches/rendering/PlantUmlRendererUtil.java @@ -23,7 +23,7 @@ public class PlantUmlRendererUtil { public static RenderResult render(RenderRequest renderRequest, RenderCacheItem cachedItem) { //或许可以不要 - prepareEnvironment(renderRequest.getSourceFilePath()); +// prepareEnvironment(renderRequest.getSourceFilePath()); String source = renderRequest.getSource(); @@ -37,22 +37,22 @@ public static RenderResult render(RenderRequest renderRequest, RenderCacheItem c // comes from Utils - @NotNull - public static void prepareEnvironment(String sourceFilePath) { -// OptionFlags.getInstance().setVerbose(LOG.isDebugEnabled()); - -// long start = System.currentTimeMillis(); - File baseDir = UIUtils.getParent(new File(sourceFilePath)); - if (baseDir != null) { - setPlantUmlDir(baseDir); - } else { - resetPlantUmlDir(); - } - - saveAllDocuments(sourceFilePath); - } +// @NotNull +// public static void prepareEnvironment(String sourceFilePath) { +//// OptionFlags.getInstance().setVerbose(LOG.isDebugEnabled()); +// +//// long start = System.currentTimeMillis(); +// File baseDir = UIUtils.getParent(new File(sourceFilePath)); +// if (baseDir != null) { +// setPlantUmlDir(baseDir); +// } else { +// resetPlantUmlDir(); +// } +// +// saveAllDocuments(sourceFilePath); +// } public static SourceStringReader newSourceStringReader(String source, RenderRequest renderRequest) { - File file = renderRequest.getSourceFile(); +// File file = renderRequest.getSourceFile(); List configAsList; String encoding; @@ -60,11 +60,11 @@ public static SourceStringReader newSourceStringReader(String source, RenderRequ configAsList = new ArrayList<>(); Defines defines; - if (file != null) { - defines = Defines.createWithFileName(file); - } else { +// if (file != null) { +// defines = Defines.createWithFileName(file); +// } else { defines = Defines.createEmpty(); - } +// } SourceStringReader sourceStringReader = new SourceStringReader(defines, source, encoding, configAsList); return sourceStringReader; } diff --git a/src/main/java/org/testshift/testcube/branches/rendering/RenderRequest.java b/src/main/java/org/testshift/testcube/branches/rendering/RenderRequest.java index d42e796..484e33b 100644 --- a/src/main/java/org/testshift/testcube/branches/rendering/RenderRequest.java +++ b/src/main/java/org/testshift/testcube/branches/rendering/RenderRequest.java @@ -5,7 +5,7 @@ import java.io.File; public class RenderRequest { - private final String sourceFilePath; +// private final String sourceFilePath; @NotNull private final String source; @NotNull @@ -19,7 +19,7 @@ public class RenderRequest { protected boolean useSettings = false; private boolean disableSvgZoom; - public RenderRequest(String sourceFilePath, + public RenderRequest(//String sourceFilePath, @NotNull String source, @NotNull ImageFormat format, int page, @@ -28,7 +28,7 @@ public RenderRequest(String sourceFilePath, Integer version, boolean renderUrlLinks, RenderCommand.Reason reason) { - this.sourceFilePath = sourceFilePath; +// this.sourceFilePath = sourceFilePath; this.source = source; this.format = format; this.page = page; @@ -38,13 +38,13 @@ public RenderRequest(String sourceFilePath, this.reason = reason; } - public String getSourceFilePath() { - return sourceFilePath; - } +// public String getSourceFilePath() { +// return sourceFilePath; +// } - public File getSourceFile() { - return new File(sourceFilePath); - } +// public File getSourceFile() { +// return new File(sourceFilePath); +// } @NotNull public String getSource() { @@ -72,9 +72,9 @@ public int getPage() { return page; } - public File getBaseDir() { - return UIUtils.getParent(new File(sourceFilePath)); - } +// public File getBaseDir() { +// return UIUtils.getParent(new File(sourceFilePath)); +// } public boolean isRenderUrlLinks() { return renderUrlLinks; diff --git a/src/main/java/org/testshift/testcube/inspect/InspectResultWithCFGAction.java b/src/main/java/org/testshift/testcube/inspect/InspectResultWithCFGAction.java new file mode 100644 index 0000000..ee2be47 --- /dev/null +++ b/src/main/java/org/testshift/testcube/inspect/InspectResultWithCFGAction.java @@ -0,0 +1,64 @@ +package org.testshift.testcube.inspect; + +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentFactory; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import org.jetbrains.annotations.NotNull; +import org.testshift.testcube.branches.CFGPanel; +import org.testshift.testcube.icons.TestCubeIcons; +import org.testshift.testcube.misc.Util; +import org.testshift.testcube.model.GenerationResult; + +public class InspectResultWithCFGAction extends NotificationAction { + private final Project project; + private final String testClass; + private final String testMethod; + private CFGPanel cfgPanel; + private String targetMethod; + + + public InspectResultWithCFGAction(Project project, String testClass, String testMethod, CFGPanel cfgPanel, + String targetMethod) { + super("Inspect amplification results"); + this.project = project; + this.testClass = testClass; + this.testMethod = testMethod; + this.targetMethod = targetMethod; + this.cfgPanel = cfgPanel; + } + + @Override + public void update(AnActionEvent e) { + // Set the availability based on whether a project is open + Project project = e.getProject(); + e.getPresentation().setEnabledAndVisible(project != null); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { + GenerationResult generationResult = GenerationResult.buildGenerationResult(project, testClass, + cfgPanel.getInitialCoveredLines(), + cfgPanel.getInitialCoveredBranches()); + + ResultWithCFGWindow resultWithCFGWindow = new ResultWithCFGWindow(cfgPanel, targetMethod, generationResult); + + ToolWindow toolWindow = ToolWindowManager.getInstance(e.getProject()).getToolWindow("Test Cube"); + if (toolWindow != null) { + ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); + Content content = contentFactory.createContent(resultWithCFGWindow.getContent(), + resultWithCFGWindow.getDisplayName(), false); + content.setCloseable(true); + content.setIcon(TestCubeIcons.AMPLIFY_TEST); + toolWindow.getContentManager().addContent(content); + toolWindow.getContentManager().setSelectedContent(content); + + toolWindow.show(); + } + } +} diff --git a/src/main/java/org/testshift/testcube/inspect/ResultWithCFGWindow.java b/src/main/java/org/testshift/testcube/inspect/ResultWithCFGWindow.java new file mode 100644 index 0000000..fcb5d1a --- /dev/null +++ b/src/main/java/org/testshift/testcube/inspect/ResultWithCFGWindow.java @@ -0,0 +1,225 @@ +package org.testshift.testcube.inspect; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiMethod; +import com.intellij.ui.JBColor; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestCaseBranchCoverageJSON; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import org.jetbrains.annotations.Contract; +import org.testshift.testcube.branches.CFGPanel; +import org.testshift.testcube.branches.rendering.RenderCommand; +import org.testshift.testcube.misc.Colors; +import org.testshift.testcube.misc.TestCubeNotifier; +import org.testshift.testcube.misc.Util; +import org.testshift.testcube.model.AmplifiedTestCase; +import org.testshift.testcube.model.GeneratedTestCase; +import org.testshift.testcube.model.GenerationResult; +import org.testshift.testcube.model.TestCase; +import org.testshift.testcube.settings.AppSettingsState; + +import java.util.List; + +import javax.swing.*; +import java.awt.*; + +public class ResultWithCFGWindow extends Component { + private JPanel amplificationResultPanel; + + private TestCaseEditorField amplifiedTestCase; + + private JPanel buttons; + private JButton add; + private JButton ignore; + private JButton next; + private JButton previous; + private JButton close; + private CFGPanel cfgPanel; + private JPanel testCasePanel; + + private int currentAmplificationTestCaseIndex; + GeneratedTestCase currentTestCase; + + private String targetMethod; + private GenerationResult generationResult; + + + public ResultWithCFGWindow(CFGPanel cfgPanel, String targetMethod, GenerationResult generationResult){ +// this(); + this.amplificationResultPanel = new JPanel(); + + this.cfgPanel = cfgPanel; + this.targetMethod = targetMethod; + this.currentAmplificationTestCaseIndex = 0; + this.generationResult = generationResult; + this.currentTestCase = generationResult.generatedTestCases.get( + currentAmplificationTestCaseIndex); + + cfgPanel.render(RenderCommand.Reason.FILE_SWITCHED); + cfgPanel.displayResult(RenderCommand.Reason.FILE_SWITCHED); + cfgPanel.maintainInitialCover(); + cfgPanel.setNewCoveredLines(currentTestCase.newCoveredLine); + cfgPanel.setNewCoveredBranches(currentTestCase.newCovredBranch); + cfgPanel.maintainNewCover(); + cfgPanel.setLayout(new GridLayout()); + + amplifiedTestCase = new TestCaseEditorField(); + amplifiedTestCase.createEditor(); + showTestCaseInEditor(currentTestCase, amplifiedTestCase); + + this.buttons = new JPanel(); + this.add = new JButton("Add Test To Test Suite"); + this.ignore = new JButton("Ignore Test Case"); + this.next = new JButton("Next Test Case"); + this.previous = new JButton("Previous Test Case"); + this.close = new JButton("Close Amplification Result"); + buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS)); + buttons.add(add); + buttons.add(ignore); + buttons.add(next); + buttons.add(previous); + buttons.add(close); + + close.addActionListener(l -> close()); + add.addActionListener(l -> addTestCaseToTestSuite()); + ignore.addActionListener(l -> ignoreTestCase()); + next.addActionListener(l -> nextTestCase()); + previous.addActionListener(l -> previousTestCase()); + this.testCasePanel = new JPanel(); + testCasePanel.setLayout(new BorderLayout()); + testCasePanel.add(amplifiedTestCase, BorderLayout.CENTER); + testCasePanel.add(buttons, BorderLayout.SOUTH); + amplificationResultPanel.setVisible(true); + amplificationResultPanel.setLayout(new BorderLayout()); + amplificationResultPanel.add(testCasePanel, BorderLayout.NORTH); + amplificationResultPanel.add(cfgPanel, BorderLayout.CENTER); + } + + private void showTestCaseInEditor(GeneratedTestCase testCase, TestCaseEditorField editor) { + editor.setText(testCase.getMethod().getText()); + } + + @Contract("false, true -> fail") + private void navigateTestCases(boolean forward, boolean removeCurrent) { + if (!forward & removeCurrent) throw new IllegalArgumentException(); + + if (removeCurrent) { + generationResult.generatedTestCases.remove(currentTestCase); + currentAmplificationTestCaseIndex--; + if (generationResult.generatedTestCases.isEmpty()) { + TestCubeNotifier notifier = new TestCubeNotifier(); + notifier.notify(generationResult.project, + "All generated test cases were added or ignored. Thank you for using Test Cube!"); + close(); + return; + } + } + if (forward) { + if (currentAmplificationTestCaseIndex + 1 == generationResult.generatedTestCases.size()) { + currentAmplificationTestCaseIndex = 0; + } else { + currentAmplificationTestCaseIndex++; + } + } else { + if (currentAmplificationTestCaseIndex == 0) { + currentAmplificationTestCaseIndex = generationResult.generatedTestCases.size() - 1; + } else { + currentAmplificationTestCaseIndex--; + } + } + currentTestCase = generationResult.generatedTestCases.get(currentAmplificationTestCaseIndex); + cfgPanel.setNewCoveredLines(currentTestCase.newCoveredLine); + cfgPanel.setNewCoveredBranches(currentTestCase.newCovredBranch); + cfgPanel.maintainNewCover(); + showTestCaseInEditor(currentTestCase, amplifiedTestCase); +// setAmplifiedInformation(); + // deal with cfgPanel + } + + public void close() { + ToolWindow toolWindow = ToolWindowManager.getInstance(generationResult.project).getToolWindow("Test Cube"); + if (toolWindow != null) { + toolWindow.getContentManager() + .removeContent(toolWindow.getContentManager().findContent(getDisplayName()), true); + if (toolWindow.getContentManager().getContentCount() == 0) { + toolWindow.hide(); + } + } + } + + public void addTestCaseToTestSuite() { + GeneratedTestCase testToAdd = currentTestCase; + + PsiMethod method = testToAdd.getMethod(); + WriteCommandAction.runWriteCommandAction(generationResult.project, () -> { + if (method != null) { + PsiMethod methodSave = (PsiMethod) method.copy(); + method.delete(); + generationResult.getOriginalClass().add(methodSave); + PsiDocumentManager.getInstance(generationResult.project).commitAllDocuments(); + + navigateTestCases(true, true); + } + }); + } + + public void deleteAmplifiedTestCaseFromFile() { + PsiMethod method = currentTestCase.getMethod(); + WriteCommandAction.runWriteCommandAction(generationResult.project, () -> { + if (method != null) { + method.delete(); + PsiDocumentManager.getInstance(generationResult.project).commitAllDocuments(); + } + }); + } + + public void ignoreTestCase() { + deleteAmplifiedTestCaseFromFile(); + navigateTestCases(true, true); + } + + public void nextTestCase() { + navigateTestCases(true, false); + } + + public void previousTestCase() { + navigateTestCases(false, false); + } + +// private void setAmplifiedInformation() { +// amplifiedInformation.setText(htmlStart() + currentTestCase.getDescription() + htmlEnd()); +// } + + private String htmlStart() { + Color foreground = JBColor.foreground(); + Color link; + if (AppSettingsState.getInstance().highlightColor.equals(Colors.DARKER)) { + link = JBColor.green.darker(); + } else { + link = JBColor.green.brighter(); + } + return "" + + ""; + } + + private String colorToRGBHtmlString(Color color) { + return "rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ")"; + } + + private String htmlEnd() { + return ""; + } + + public JComponent getContent() { + return amplificationResultPanel; + } + + public String getDisplayName() { + return "Test Generation for " + targetMethod+ "()'"; + } +} diff --git a/src/main/java/org/testshift/testcube/inspect/TestCaseEditorField.java b/src/main/java/org/testshift/testcube/inspect/TestCaseEditorField.java index 2fd2486..3053207 100644 --- a/src/main/java/org/testshift/testcube/inspect/TestCaseEditorField.java +++ b/src/main/java/org/testshift/testcube/inspect/TestCaseEditorField.java @@ -26,8 +26,7 @@ protected EditorEx createEditor() { EditorSettings settings = editor.getSettings(); settings.setLineNumbersShown(true); - settings.setAdditionalPageAtBottom(true); - + settings.setAdditionalPageAtBottom(false); return editor; } } diff --git a/src/main/java/org/testshift/testcube/misc/Util.java b/src/main/java/org/testshift/testcube/misc/Util.java index 16cf2bb..14b4d30 100644 --- a/src/main/java/org/testshift/testcube/misc/Util.java +++ b/src/main/java/org/testshift/testcube/misc/Util.java @@ -5,12 +5,18 @@ import com.intellij.openapi.project.ProjectUtil; import com.intellij.psi.PsiMethod; import com.intellij.psi.util.ClassUtil; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; import eu.stamp_project.dspot.common.report.output.selector.extendedcoverage.json.TestClassJSON; +import eu.stamp_project.dspot.selector.branchcoverageselector.BranchCoverage; +import eu.stamp_project.dspot.selector.branchcoverageselector.LineCoverage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.HashSet; +import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.regex.Matcher; public class Util { @@ -49,6 +55,58 @@ public static TestClassJSON getResultJSON(Project project, String testClass) { return null; } + public static TestClassBranchCoverageJSON getBranchCoverageJSON(Project project, String testClass) { + Gson gson = new Gson(); + try { + return gson.fromJson(new FileReader( + project.getBasePath() + Config.OUTPUT_PATH_DSPOT + File.separator + testClass + "_report.json"), + TestClassBranchCoverageJSON.class); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + public static Set getInitialCoveredLine(TestClassBranchCoverageJSON result){ + Set coveredLines = new HashSet<>(); + List lineCoverages = result.getInitialLineCoverage(); + for(LineCoverage lineCoverage: lineCoverages){ + coveredLines.add(lineCoverage.getLine()+""); + } + return coveredLines; + } + + public static class Branch { + private String line; + private String symbol; + public Branch(String line, String symbol){ + this.line = line; + this.symbol = symbol; + } + + public String getLine() { + return line; + } + + public String getSymbol() { + return symbol; + } + } + + public static Set getInitialCoveredBranch(TestClassBranchCoverageJSON result){ + Set coveredBranches = new HashSet<>(); + List branchCoverages = result.getInitialBranchCoverage(); + for(BranchCoverage branchCoverage: branchCoverages){ + if(branchCoverage.getTrueHitCount()>0){ + coveredBranches.add(new Branch(branchCoverage.getRegion().getStartLine() + "", "True")); + } + if(branchCoverage.getFalseHitCount()>0){ + coveredBranches.add(new Branch(branchCoverage.getRegion().getStartLine() + "", "False")); + } + } + return coveredBranches; + } + public static boolean matchMethodNameAndDescriptor(PsiMethod psiMethod, String name, String descriptor) { return psiMethod.getName().equals(name) && ClassUtil.getAsmMethodSignature(psiMethod).equals(descriptor); } diff --git a/src/main/java/org/testshift/testcube/model/GeneratedTestCase.java b/src/main/java/org/testshift/testcube/model/GeneratedTestCase.java new file mode 100644 index 0000000..732f305 --- /dev/null +++ b/src/main/java/org/testshift/testcube/model/GeneratedTestCase.java @@ -0,0 +1,47 @@ +package org.testshift.testcube.model; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.PsiMethod; +import org.jetbrains.annotations.Nullable; +import org.testshift.testcube.inspect.AmplificationResultWindow; +import org.testshift.testcube.inspect.ResultWithCFGWindow; +import org.testshift.testcube.misc.Util; + +import java.util.Arrays; +import java.util.Set; + +public class GeneratedTestCase{ + private static final Logger logger = Logger.getInstance(ResultWithCFGWindow.class); + public String filePath; + private String name; + private PsiMethod method; + public PsiJavaFile psiFile; + public Set newCoveredLine; + public Set newCovredBranch; + public int assertionsAdded; + public int inputAdded; + + public GeneratedTestCase(String filePath, String name, PsiMethod method, PsiJavaFile psiFile, int assertionsAdded + , int inputAdded, Set newCoveredLine, Set newCovredBranch) { + this.filePath = filePath; + this.name = name; + this.method = method; + this.psiFile = psiFile; + this.assertionsAdded = assertionsAdded; + this.inputAdded = inputAdded; + this.newCoveredLine = newCoveredLine; + this.newCovredBranch = newCovredBranch; + } + + public PsiMethod getMethod() { + return method; + } + + public String getDescription() { + return "Generated test case '" + name + "'

" + "Input modifications: " + inputAdded + "
" + + "Assert statements added: " + assertionsAdded + "

" + "New branch covered: " + newCovredBranch.size() + "

" + + "New line covered: " + newCoveredLine.size(); + } +} diff --git a/src/main/java/org/testshift/testcube/model/GenerationResult.java b/src/main/java/org/testshift/testcube/model/GenerationResult.java new file mode 100644 index 0000000..7d30a9e --- /dev/null +++ b/src/main/java/org/testshift/testcube/model/GenerationResult.java @@ -0,0 +1,138 @@ +package org.testshift.testcube.model; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestCaseBranchCoverageJSON; +import eu.stamp_project.dspot.common.report.output.selector.branchcoverage.json.TestClassBranchCoverageJSON; +import eu.stamp_project.dspot.common.report.output.selector.extendedcoverage.json.TestCaseJSON; +import eu.stamp_project.dspot.selector.branchcoverageselector.BranchCoverage; +import eu.stamp_project.dspot.selector.branchcoverageselector.LineCoverage; +import eu.stamp_project.dspot.selector.extendedcoverageselector.CoverageImprovement; +import eu.stamp_project.dspot.selector.extendedcoverageselector.ExtendedCoverage; +import org.testshift.testcube.misc.Util; + +import java.util.*; + +public class GenerationResult { + private static final Logger logger = Logger.getInstance(GenerationResult.class); + public Project project; + public String testClass; + public Set initialCoveredLines; + private Set initialCoveredBranches;; + public List generatedTestCases = new ArrayList<>(); + private PsiClass originalClass; + + private GenerationResult(Project project, String testClass, Set initialCoveredLines, + Set initialCoveredBranches) { + this.project = project; + this.testClass = testClass; + this.initialCoveredLines = initialCoveredLines; + this.initialCoveredBranches = initialCoveredBranches; + } + public static GenerationResult buildGenerationResult(Project project, String testClass, + Set initialCoveredLines, Set initialCoveredBranches){ + GenerationResult result = new GenerationResult(project, testClass, initialCoveredLines, + initialCoveredBranches); + TestClassBranchCoverageJSON coverageResult = (TestClassBranchCoverageJSON) Util.getBranchCoverageJSON(project, + testClass); + if (coverageResult == null) { + logger.warn("Json result file not found!"); + return result; + } + + String originalTestClassPath = Util.getOriginalTestClassPath(project, testClass); + VirtualFile originalFile = LocalFileSystem.getInstance().findFileByPath(originalTestClassPath); + if (originalFile != null) { + PsiJavaFile psiFile = (PsiJavaFile) JavaPsiFacade.getInstance(project) + .findClass(testClass, + GlobalSearchScope.everythingScope(result.project)) + .getContainingFile(); + result.originalClass = Arrays.stream(psiFile.getClasses()) + .filter((PsiClass c) -> c.getQualifiedName().equals(testClass)) + .findFirst() + .get(); + } + + + List testCaseBranchCoverageJSONList = coverageResult.getTestCases(); + + String amplifiedTestClassPath = Util.getAmplifiedTestClassPath(project, testClass); + VirtualFile file = LocalFileSystem.getInstance().findFileByPath(amplifiedTestClassPath); + + if (file != null) { + PsiJavaFile psiFile = (PsiJavaFile) PsiManager.getInstance(project).findFile(file); + if (psiFile != null) { + PsiClass psiClass = Arrays.stream(psiFile.getClasses()) + .filter((PsiClass c) -> c.getQualifiedName().equals(testClass)) + .findFirst() + .get(); + PsiMethod[] methods = psiClass.getMethods(); + + if (methods.length != testCaseBranchCoverageJSONList.size()) { + logger.warn("Count of methods found in amplified class: " + methods.length + " does not match " + + "with match with count of amplified methods reported: " + + testCaseBranchCoverageJSONList.size()); + } + + for (PsiMethod method : methods) { + Optional testCaseJSON = testCaseBranchCoverageJSONList + .stream() + .filter(tcj -> tcj.getName() + .equals(method.getName())) + .findAny(); + + Set newCoveredLines = computeNewCoveredLines(testCaseJSON.get(), initialCoveredLines); + Set newCoveredBranches = computeNewCoveredBranches(testCaseJSON.get(), + initialCoveredBranches); + + if (testCaseJSON.isPresent()) { + result.generatedTestCases.add( + new GeneratedTestCase(amplifiedTestClassPath, method.getName(), method, psiFile, + testCaseJSON.get().getNbAssertionAdded(), + testCaseJSON.get().getNbInputAdded(), newCoveredLines, + newCoveredBranches)); + } else { + logger.warn("Found no matching json result for test case " + method.getName()); + } + } + } + } + return result; + } + + private static Set computeNewCoveredLines(TestCaseBranchCoverageJSON testCaseJSON, Set initialCoveredLines){ + Set newCoveredLines = new HashSet<>(); + List lineCoverages = testCaseJSON.getLineCoverageList(); + for(LineCoverage lineCoverage: lineCoverages){ + if(!initialCoveredLines.contains(lineCoverage.getLine())){ + newCoveredLines.add(lineCoverage.getLine()+""); + } + } + return newCoveredLines; + } + + private static Set computeNewCoveredBranches(TestCaseBranchCoverageJSON testCaseJSON, + Set initialCoveredBranches){ + Set newCoveredBranches = new HashSet<>(); + List branchCoverages = testCaseJSON.getBranchCoverageList(); + for(BranchCoverage branchCoverage: branchCoverages){ + Util.Branch branchTrue = new Util.Branch(branchCoverage.getRegion().getStartLine()+"", "True"); + if(branchCoverage.getTrueHitCount()>0 && !initialCoveredBranches.contains(branchTrue)){ + newCoveredBranches.add(branchTrue); + } + Util.Branch branchFalse = new Util.Branch(branchCoverage.getRegion().getStartLine()+"", "False"); + if(branchCoverage.getFalseHitCount()>0 && !initialCoveredBranches.contains(branchFalse)){ + newCoveredBranches.add(branchFalse); + } + } + return newCoveredBranches; + } + + public PsiClass getOriginalClass() { + return originalClass; + } +} diff --git a/src/main/java/org/testshift/testcube/settings/AppSettingsConfigurable.java b/src/main/java/org/testshift/testcube/settings/AppSettingsConfigurable.java index dd669a8..aba9ff8 100644 --- a/src/main/java/org/testshift/testcube/settings/AppSettingsConfigurable.java +++ b/src/main/java/org/testshift/testcube/settings/AppSettingsConfigurable.java @@ -16,7 +16,7 @@ public class AppSettingsConfigurable implements Configurable { // A default constructor with no arguments is required because this implementation // is registered as an applicationConfigurable EP - @Nls(capitalization = Nls.Capitalization.Title) +// @Nls(capitalization = Nls.Capitalization.Title) @Override public String getDisplayName() { return "Test Cube Settings"; diff --git a/src/main/resources/dspot/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar b/src/main/resources/dspot/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar index 6c6016a..8decc76 100644 Binary files a/src/main/resources/dspot/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar and b/src/main/resources/dspot/dspot-3.1.1-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/src/main/resources/test.puml b/src/main/resources/test.puml index dbba627..0437a7f 100644 --- a/src/main/resources/test.puml +++ b/src/main/resources/test.puml @@ -2,12 +2,12 @@ 'https://plantuml.com/activity-diagram-beta start -:ClickServlet.handleRequest(); -:new page; +:0 ClickServlet.handleRequest(); +:1 new page; if (Page.onSecurityCheck) then (true) :Page.onInit(); a; b; if (isForward?) then (no) - :Process controls; + :Process controls; 3 test; if (continue processing?) then (no) stop endif