From 3d0e9bc228644103a5de48f9d1a52251e6bcc03b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:06:41 +0300 Subject: [PATCH 01/12] [git] Display rebase error right in the notification Don't tell user to go to the VCS Console. Use the new GitImpl variant to call Git command instead of the obsolete GitTask. Also fix the situation when workingTreeChangeStarted was actually reported after rebase command completed. --- .../git4idea/actions/GitRebaseActionBase.java | 44 ++++++++----------- .../git4idea/src/git4idea/commands/Git.java | 8 ++++ .../src/git4idea/commands/GitImpl.java | 18 ++++++-- .../src/git4idea/i18n/GitBundle.properties | 1 - 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java index af0993eef262..9652625f5c68 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java +++ b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java @@ -17,16 +17,17 @@ package git4idea.actions; import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitUtil; +import git4idea.commands.Git; +import git4idea.commands.GitCommandResult; import git4idea.commands.GitLineHandler; -import git4idea.commands.GitTask; -import git4idea.commands.GitTaskResult; -import git4idea.commands.GitTaskResultHandlerAdapter; import git4idea.i18n.GitBundle; import git4idea.rebase.GitInteractiveRebaseEditorHandler; import git4idea.rebase.GitRebaseEditorService; @@ -50,7 +51,7 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { @NotNull final VirtualFile defaultRoot, final Set affectedRoots, final List exceptions) throws VcsException { - GitLineHandler h = createHandler(project, gitRoots, defaultRoot); + final GitLineHandler h = createHandler(project, gitRoots, defaultRoot); if (h == null) { return; } @@ -63,34 +64,32 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { affectedRoots.add(root); service.configureHandler(h, editor.getHandlerNo()); - GitTask task = new GitTask(project, h, GitBundle.getString("rebasing.title")); - task.executeInBackground(false, new GitTaskResultHandlerAdapter() { + new Task.Backgroundable(project, GitBundle.getString("rebasing.title"), false) { @Override - protected void run(GitTaskResult taskResult) { + public void run(@NotNull ProgressIndicator indicator) { AccessToken token = DvcsUtil.workingTreeChangeStarted(project); try { + GitCommandResult result = ServiceManager.getService(Git.class).runCommand(h); editor.close(); GitRepositoryManager manager = GitUtil.getRepositoryManager(project); manager.updateRepository(root); root.refresh(false, true); - notifyAboutResult(taskResult, resultListener, exceptions, project); + notifyAboutResult(result, resultListener, exceptions, project); } finally { DvcsUtil.workingTreeChangeFinished(project, token); } } - }); + }.queue(); } - private static void notifyAboutResult(GitTaskResult taskResult, - GitRebaseLineListener resultListener, - List exceptions, - Project project) { - if (taskResult == GitTaskResult.CANCELLED) { - return; - } + private static void notifyAboutResult(@NotNull GitCommandResult commandResult, + @NotNull GitRebaseLineListener resultListener, + @NotNull List exceptions, + @NotNull Project project) { final GitRebaseLineListener.Result result = resultListener.getResult(); String messageId; + String message = null; boolean isError = true; switch (result.status) { case CONFLICT: @@ -98,6 +97,7 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { break; case ERROR: messageId = "rebase.result.error"; + message = commandResult.getErrorOutputAsHtmlString(); break; case CANCELLED: // we do not need to show a message if editing was cancelled. @@ -115,15 +115,9 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { messageId = null; } - String message; - String title; - if (messageId != null) { + String title = GitBundle.message(messageId + ".title"); + if (message == null) { message = GitBundle.message(messageId, result.current, result.total); - title = GitBundle.message(messageId + ".title"); - } - else { - message = "Rebase finished: " + StringUtil.capitalize(StringUtil.toLowerCase(result.status.name())); - title = ""; } if (isError) { diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index c56a3d729fe0..5b7aca309edc 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -42,6 +42,14 @@ public interface Git { @NotNull GitCommandResult runCommand(@NotNull Computable handlerConstructor); + /** + * A generic method to run a Git command, when existing methods are not sufficient.
+ * Can be used instead of {@link #runCommand(Computable)} if the operation will not need to be repeated for sure + * (e.g. it is a completely local operation). + */ + @NotNull + GitCommandResult runCommand(@NotNull GitLineHandler handler); + @NotNull GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners); diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index a658879a9ed1..93ec7cde808b 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -47,6 +47,7 @@ import static java.util.Collections.singleton; * * @author Kirill Likhodedov */ +@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale") public class GitImpl implements Git { private final Logger LOG = Logger.getInstance(Git.class); @@ -624,13 +625,24 @@ public class GitImpl implements Git { public GitCommandResult runCommand(@NotNull Computable handlerConstructor) { return run(handlerConstructor); } - + + @NotNull + @Override + public GitCommandResult runCommand(@NotNull final GitLineHandler handler) { + return runCommand(new Computable() { + @Override + public GitLineHandler compute() { + return handler; + } + }); + } + /** * Check if the line looks line an error message */ private static boolean isError(String text) { for (String indicator : ERROR_INDICATORS) { - if (text.startsWith(indicator.toLowerCase())) { + if (text.trim().toLowerCase().startsWith(indicator.toLowerCase())) { return true; } } @@ -640,7 +652,7 @@ public class GitImpl implements Git { // could be upper-cased, so should check case-insensitively public static final String[] ERROR_INDICATORS = { "error", "remote: error", "fatal", - "Cannot apply", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict", + "Cannot", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict", "unable" }; } diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 7b1ad1821161..19d32daf953d 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -290,7 +290,6 @@ rebase.result.cancelled=The rebase process was cancelled. rebase.result.conflict.title=Rebase Suspended rebase.result.conflict=Stopped rebasing because of conflict ({0}/{1}).\nResolve conflicts and continue the rebase process. rebase.result.error.title=Rebase Error -rebase.result.error=Stopped rebasing because of error while rebasing ({0}/{1}).\nCheck vcs console for details. rebase.show.remote.branches.tooltip=If selected, remote branches are shown in drop down as well. rebase.show.remote.branches=Show Re&mote Branches rebase.show.tags.tooltip=Show tags in \"from\" and \"onto\" comboboxes. From 51ac666d43718d5ecc52d1b2fec7c3663deb40c4 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:10:27 +0300 Subject: [PATCH 02/12] [git] rebase dialog: focus "onto" field by default --- plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 649c25486ce6..51c86a93bdbc 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java @@ -33,6 +33,7 @@ import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.ui.GitReferenceValidator; import git4idea.util.GitUIUtil; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -162,6 +163,12 @@ public class GitRebaseDialog extends DialogWrapper { validateFields(); } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myOntoComboBox; + } + public GitLineHandler handler() { GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.REBASE); h.setStdoutSuppressed(false); From 043e5d22d4a7d93bc0ad9e0a6fed9a7564a1cf58 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:22:37 +0300 Subject: [PATCH 03/12] [git] IDEA-137632 speed search highlighting in interactive rebase --- .../git4idea/src/git4idea/rebase/GitRebaseEditor.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index 7514aa940bbe..f14ed780c58d 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -22,7 +22,9 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.Cell; +import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.TableSpeedSearch; +import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.ArrayUtil; import com.intellij.util.ListWithSelection; @@ -132,6 +134,14 @@ public class GitRebaseEditor extends DialogWrapper { actionColumn.setCellEditor(new DefaultCellEditor(editorComboBox)); actionColumn.setCellRenderer(ComboBoxTableCellRenderer.INSTANCE); + myCommitsTable.setDefaultRenderer(String.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + append(value.toString()); + SpeedSearchUtil.applySpeedSearchHighlighting(myCommitsTable, this, true, selected); + } + }); + myCommitsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(final ListSelectionEvent e) { myViewButton.setEnabled(myCommitsTable.getSelectedRowCount() == 1); From 6d405bc74c0ee9d9a31a522bf828e5b6f85a6242 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 11:09:03 +0200 Subject: [PATCH 04/12] testng: preserve order in which tests are found --- .../testng/src/com/theoryinpractice/testng/util/TestNGUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 38fd89d5e695..0375b4a1b260 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -436,7 +436,7 @@ public class TestNGUtil { public void run() { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - final Collection set = new HashSet(); + final Collection set = new LinkedHashSet(); PsiManager manager = PsiManager.getInstance(filter.getProject()); GlobalSearchScope scope = filter.getScope(); GlobalSearchScope projectScope = GlobalSearchScope.projectScope(manager.getProject()); From 538db34fc0d3962eb9c9eb35efcc216d76e4399e Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 12:18:07 +0200 Subject: [PATCH 05/12] testng: extract strategy --- .../configuration/SearchingForTestsTask.java | 342 +----------------- .../configuration/TestNGConfiguration.java | 73 +--- .../TestNGConfigurationProducer.java | 3 +- .../TestNGConfigurationType.java | 4 +- .../testng/model/TestData.java | 49 +-- .../testng/model/TestNGTestClass.java | 95 +++++ .../testng/model/TestNGTestGroup.java | 63 ++++ .../testng/model/TestNGTestMethod.java | 110 ++++++ .../testng/model/TestNGTestObject.java | 247 +++++++++++++ .../testng/model/TestNGTestPackage.java | 103 ++++++ .../testng/model/TestNGTestPattern.java | 145 ++++++++ .../testng/model/TestNGTestSuite.java | 61 ++++ 12 files changed, 847 insertions(+), 448 deletions(-) create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java index da4701d312ad..5dade70f8fce 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java @@ -20,40 +20,31 @@ */ package com.theoryinpractice.testng.configuration; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.execution.CantRunException; import com.intellij.execution.ExecutionException; import com.intellij.execution.testframework.SearchForTestsTask; -import com.intellij.execution.testframework.TestSearchScope; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PackageScope; -import com.intellij.psi.search.searches.AnnotatedMembersSearch; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.intellij.psi.util.ClassUtil; import com.theoryinpractice.testng.model.IDEARemoteTestRunnerClient; -import com.theoryinpractice.testng.model.TestClassFilter; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import com.theoryinpractice.testng.model.TestType; import com.theoryinpractice.testng.util.TestNGUtil; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.testng.xml.*; import java.io.*; import java.net.ServerSocket; import java.util.*; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; public class SearchingForTestsTask extends SearchForTestsTask { private static final Logger LOG = Logger.getInstance("#" + SearchingForTestsTask.class.getName()); @@ -280,182 +271,9 @@ public class SearchingForTestsTask extends SearchForTestsTask { protected void fillTestObjects(final Map>> classes) throws CantRunException { - final TestData data = myConfig.getPersistantData(); - final PsiManager psiManager = PsiManager.getInstance(myProject); - if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) { - final String packageName = data.getPackageName(); - PsiPackage psiPackage = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiPackage compute() { - return JavaPsiFacade.getInstance(psiManager.getProject()).findPackage(packageName); - } - } - ); - if (psiPackage == null) { - throw CantRunException.packageNotFound(packageName); - } - else { - TestSearchScope scope = myConfig.getPersistantData().getScope(); - //TODO we should narrow this down by module really, if that's what's specified - TestClassFilter projectFilter = - new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true); - TestClassFilter filter = projectFilter.intersectionWith(PackageScope.packageScope(psiPackage, true)); - calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(filter, false)); - if (classes.size() == 0) { - throw new CantRunException("No tests found in the package \"" + packageName + '\"'); - } - } - } - else if (data.TEST_OBJECT.equals(TestType.CLASS.getType())) { - //it's a class - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); - } - } - ); - if (psiClass == null) { - throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); - } - if (null == ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - public String compute() { - return psiClass.getQualifiedName(); - } - })) { - throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); - } - calculateDependencies(null, classes, psiClass); - } - else if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) { - //it's a method - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); - } - } - ); - if (psiClass == null) { - throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); - } - if (null == ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public String compute() { - return psiClass.getQualifiedName(); - } - } - )) { - throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); - } - collectTestMethods(classes, psiClass, data.getMethodName()); - } - else if (data.TEST_OBJECT.equals(TestType.GROUP.getType())) { - //for a group, we include all classes - PsiClass[] testClasses = TestNGUtil - .getAllTestClasses(new TestClassFilter(data.getScope().getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true), false); - if (testClasses != null) { - for (PsiClass c : testClasses) { - classes.put(c, new LinkedHashMap>()); - } - } - } - else if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) { - for (final String pattern : data.getPatterns()) { - final String className; - final String methodName; - if (pattern.contains(",")) { - methodName = StringUtil.getShortName(pattern, ','); - className = StringUtil.getPackageName(pattern, ','); - } else { - className = pattern; - methodName = null; - } - - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - @Override - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, className.replace('/', '.'), null, true, getSearchScope()); - } - }); - if (psiClass != null) { - final Boolean hasTest = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - return TestNGUtil.hasTest(psiClass); - } - }); - if (hasTest) { - if (StringUtil.isEmpty(methodName)) { - calculateDependencies(null, classes, psiClass); - } - else { - collectTestMethods(classes, psiClass, methodName); - } - } else { - throw new CantRunException("No tests found in class " + className); - } - } - } - if (classes.size() != data.getPatterns().size()) { - TestSearchScope scope = myConfig.getPersistantData().getScope(); - final List compilePatterns = new ArrayList(); - for (String p : data.getPatterns()) { - final Pattern compilePattern; - try { - compilePattern = Pattern.compile(p); - } - catch (PatternSyntaxException e) { - continue; - } - if (compilePattern != null) { - compilePatterns.add(compilePattern); - } - } - TestClassFilter projectFilter = - new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true){ - @Override - public boolean isAccepted(PsiClass psiClass) { - if (super.isAccepted(psiClass)) { - final String qualifiedName = psiClass.getQualifiedName(); - LOG.assertTrue(qualifiedName != null); - for (Pattern pattern : compilePatterns) { - if (pattern.matcher(qualifiedName).matches()) return true; - } - } - return false; - } - }; - calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(projectFilter, false)); - if (classes.size() == 0) { - throw new CantRunException("No tests found in for patterns \"" + StringUtil.join(data.getPatterns(), " || ") + '\"'); - } - } - } - } - - private void collectTestMethods(Map>> classes, final PsiClass psiClass, final String methodName) { - final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( - new Computable() { - public PsiMethod[] compute() { - return psiClass.findMethodsByName(methodName, true); - } - } - ); - calculateDependencies(methods, classes, psiClass); - Map> psiMethods = classes.get(psiClass); - if (psiMethods == null) { - psiMethods = new LinkedHashMap>(); - classes.put(psiClass, psiMethods); - } - for (PsiMethod method : methods) { - psiMethods.put(method, Collections.emptyList()); + final TestNGTestObject testObject = TestNGTestObject.fromConfig(myConfig); + if (testObject != null) { + testObject.fillTestObjects(classes); } } @@ -492,152 +310,4 @@ public class SearchingForTestsTask extends SearchForTestsTask { return params; } - private void calculateDependencies(PsiMethod[] methods, - final Map>> results, - @Nullable final PsiClass... classes) { - calculateDependencies(methods, results, new LinkedHashSet(), classes); - } - - private void calculateDependencies(final PsiMethod[] methods, - final Map>> results, - final Set alreadyMarkedToBeChecked, - @Nullable final PsiClass... classes) { - if (classes != null && classes.length > 0) { - final Set groupDependencies = new LinkedHashSet(); - TestNGUtil.collectAnnotationValues(groupDependencies, "dependsOnGroups", methods, classes); - final Set membersToCheckNow = new LinkedHashSet(); - if (!groupDependencies.isEmpty()) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - final Project project = classes[0].getProject(); - final PsiClass testAnnotation = - JavaPsiFacade.getInstance(project).findClass(TestNGUtil.TEST_ANNOTATION_FQN, GlobalSearchScope.allScope(project)); - LOG.assertTrue(testAnnotation != null); - for (PsiMember psiMember : AnnotatedMembersSearch.search(testAnnotation, getSearchScope())) { - final PsiAnnotation annotation = AnnotationUtil.findAnnotation(psiMember, TestNGUtil.TEST_ANNOTATION_FQN); - if (TestNGUtil.isAnnotatedWithParameter(annotation, "groups", groupDependencies)) { - if (appendMember(psiMember, alreadyMarkedToBeChecked, results)) { - membersToCheckNow.add(psiMember); - } - } - } - } - }); - } - - collectDependsOnMethods(results, alreadyMarkedToBeChecked, membersToCheckNow, methods, classes); - - if (methods == null) { - for (PsiClass c : classes) { - results.put(c, new LinkedHashMap>()); - } - } else { - for (PsiMember psiMember : membersToCheckNow) { - PsiClass psiClass; - PsiMethod[] meths = null; - if (psiMember instanceof PsiMethod) { - psiClass = psiMember.getContainingClass(); - meths = new PsiMethod[] {(PsiMethod)psiMember}; - } else { - psiClass = (PsiClass)psiMember; - } - calculateDependencies(meths, results, alreadyMarkedToBeChecked, psiClass); - } - } - } - } - - private static void collectDependsOnMethods(final Map>> results, - final Set alreadyMarkedToBeChecked, - final Set membersToCheckNow, - final PsiMethod[] methods, - final PsiClass... classes) { - final PsiClass[] psiClasses; - if (methods != null && methods.length > 0) { - final Set containingClasses = new LinkedHashSet(); - for (final PsiMethod method : methods) { - containingClasses.add(ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiClass compute() { - return method.getContainingClass(); - } - })); - } - psiClasses = containingClasses.toArray(new PsiClass[containingClasses.size()]); - } else { - psiClasses = classes; - } - for (final PsiClass containingClass : psiClasses) { - final Set testMethodDependencies = new LinkedHashSet(); - TestNGUtil.collectAnnotationValues(testMethodDependencies, "dependsOnMethods", methods, containingClass); - if (!testMethodDependencies.isEmpty()) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - final Project project = containingClass.getProject(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - for (String dependency : testMethodDependencies) { - final String className = StringUtil.getPackageName(dependency); - final String methodName = StringUtil.getShortName(dependency); - if (StringUtil.isEmpty(className)) { - checkClassMethods(methodName, containingClass, alreadyMarkedToBeChecked, membersToCheckNow, results); - } - else { - final PsiClass aClass = psiFacade.findClass(className, containingClass.getResolveScope()); - if (aClass != null) { - checkClassMethods(methodName, aClass, alreadyMarkedToBeChecked, membersToCheckNow, results); - } - } - } - } - }); - } - } - } - - private static void checkClassMethods(String methodName, - PsiClass containingClass, - Set alreadyMarkedToBeChecked, - Set membersToCheckNow, - Map>> results) { - final PsiMethod[] psiMethods = containingClass.findMethodsByName(methodName, true); - for (PsiMethod method : psiMethods) { - if (AnnotationUtil.isAnnotated(method, TestNGUtil.TEST_ANNOTATION_FQN, false) && - appendMember(method, alreadyMarkedToBeChecked, results)) { - membersToCheckNow.add(method); - } - } - } - - private static boolean appendMember(final PsiMember psiMember, - final Set underConsideration, - final Map>> results) { - boolean result = false; - final PsiClass psiClass = psiMember instanceof PsiClass ? ((PsiClass)psiMember) : psiMember.getContainingClass(); - Map> psiMethods = results.get(psiClass); - if (psiMethods == null) { - psiMethods = new LinkedHashMap>(); - results.put(psiClass, psiMethods); - if (psiMember instanceof PsiClass) { - result = underConsideration.add(psiMember); - } - } - if (psiMember instanceof PsiMethod) { - final boolean add = psiMethods.put((PsiMethod)psiMember, Collections.emptyList()) != null; - if (add) { - return underConsideration.add(psiMember); - } - return false; - } - return result; - } - - @NotNull - private GlobalSearchScope getSearchScope() { - final TestData data = myConfig.getPersistantData(); - final Module module = myConfig.getConfigurationModule().getModule(); - return data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) - ? myConfig.getPersistantData().getScope().getSourceScope(myConfig).getGlobalSearchScope() - : module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(myConfig.getProject()); - } - } diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java index e051d95331e8..376e74bfcb1d 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java @@ -29,7 +29,6 @@ import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.execution.configurations.*; import com.intellij.execution.junit.RefactoringListeners; import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.execution.testframework.SourceScope; import com.intellij.execution.testframework.TestSearchScope; import com.intellij.execution.util.JavaParametersUtil; import com.intellij.execution.util.ProgramParametersUtil; @@ -47,12 +46,12 @@ import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.listeners.UndoRefactoringElementListener; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import com.theoryinpractice.testng.model.TestType; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.testng.xml.Parser; import java.util.*; @@ -68,7 +67,7 @@ public class TestNGConfiguration extends ModuleBasedConfiguration 0) { - return "Tests in \"" + data.getPackageName() + '\"'; - } - else { - return "All Tests"; - } - } - if (TestType.METHOD.getType().equals(data.TEST_OBJECT)) { - return data.getMethodName() + "()"; - } - if (TestType.SUITE.getType().equals(data.TEST_OBJECT)) { - return data.getSuiteName(); - } - return data.getGroupName(); + final TestNGTestObject testObject = TestNGTestObject.fromConfig(this); + return testObject != null ? ProgramRunnerUtil.shortenName(testObject.getActionName(), 0) : null; } public void setVMParameters(String value) { @@ -299,45 +280,9 @@ public class TestNGConfiguration extends ModuleBasedConfiguration patterns = data.getPatterns(); - if (patterns.isEmpty()) { - throw new RuntimeConfigurationWarning("No pattern selected"); - } + final TestNGTestObject testObject = TestNGTestObject.fromConfig(this); + if (testObject != null) { + testObject.checkConfiguration(); } JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this); ProgramParametersUtil.checkWorkingDirectoryExist(this, getProject(), getConfigurationModule().getModule()); diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java index 5203f6c09819..f5dfadb86aef 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java @@ -33,6 +33,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import org.testng.IDEATestNGRemoteListener; public abstract class TestNGConfigurationProducer extends JavaRunConfigurationProducerBase implements Cloneable { @@ -58,7 +59,7 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr String paramSetName = contextLocation instanceof PsiMemberParameterizedLocation ? getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()) : null; if (paramSetName != null && !Comparing.strEqual(paramSetName, testNGConfiguration.getProgramParameters())) return false; - TestData testobject = testNGConfiguration.getPersistantData(); + TestNGTestObject testobject = TestNGTestObject.fromConfig(testNGConfiguration); if (testobject != null) { if (testobject.isConfiguredByElement(element)) { final Module configurationModule = testNGConfiguration.getConfigurationModule().getModule(); diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java index 3b7829dcb8b2..882e61c53b22 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java @@ -33,6 +33,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import icons.TestngIcons; import org.jetbrains.annotations.NotNull; @@ -72,7 +73,8 @@ public class TestNGConfigurationType implements ConfigurationType return false; else { final PsiElement element = location.getPsiElement(); - if (testobject.isConfiguredByElement(element)) { + final TestNGTestObject testNGTestObject = TestNGTestObject.fromConfig(config); + if (testNGTestObject != null && testNGTestObject.isConfiguredByElement(element)) { final Module configurationModule = config.getConfigurationModule().getModule(); if (Comparing.equal(location.getModule(), configurationModule)) return true; diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java index 2a75977483bb..48d925fbf93b 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java @@ -18,16 +18,15 @@ package com.theoryinpractice.testng.model; import com.intellij.execution.ExternalizablePath; import com.intellij.execution.JavaExecutionUtil; import com.intellij.execution.Location; -import com.intellij.execution.configurations.JavaRunConfigurationModule; import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.TestSearchScope; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiPackage; import java.util.*; @@ -174,26 +173,6 @@ public class TestData implements Cloneable return data; } - public String getGeneratedName(JavaRunConfigurationModule runconfigurationmodule) { - if (TestType.PACKAGE.getType().equals(TEST_OBJECT)) if (getPackageName().length() == 0) return ""; - else return getPackageName(); - String name = JavaExecutionUtil.getPresentableClassName(getMainClassName()); - if (TestType.METHOD.getType().equals(TEST_OBJECT)) { - return name + '.' + getMethodName(); - } - else if (TestType.SUITE.getType().equals(TEST_OBJECT)) { - return getSuiteName(); - } - else { - if (TestType.PATTERN.getType().equals(TEST_OBJECT)) { - final int size = myPatterns.size(); - if (size == 0) return "Temp suite"; - return StringUtil.getShortName(myPatterns.iterator().next()) + (size > 1 ? " and " + (size - 1) + " more" : ""); - } - return name; - } - } - public String getMainClassName() { return MAIN_CLASS_NAME == null ? "" : MAIN_CLASS_NAME; } @@ -217,28 +196,6 @@ public class TestData implements Cloneable return JavaExecutionUtil.findModule(psiclass); } - public boolean isConfiguredByElement(PsiElement element) { - if (TEST_OBJECT.equals(TestType.PACKAGE.getType())) { - if (element instanceof PsiPackage) { - return Comparing.strEqual(PACKAGE_NAME, ((PsiPackage) element).getQualifiedName()); - } else if (element instanceof PsiDirectory) { - final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(((PsiDirectory)element)); - return psiPackage != null && Comparing.strEqual(PACKAGE_NAME, psiPackage.getQualifiedName()); - } - } - - element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); - if (element instanceof PsiMethod && TEST_OBJECT.equals(TestType.METHOD.getType())) { - final PsiClass aClass = ((PsiMethod) element).getContainingClass(); - return aClass != null && - Comparing.strEqual(MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName(aClass)) && - Comparing.strEqual(METHOD_NAME, ((PsiMethod) element).getName()); - } else if (element instanceof PsiClass && TEST_OBJECT.equals(TestType.CLASS.getType())) { - return Comparing.strEqual(MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName((PsiClass) element)); - } - return false; - } - public Map getEnvs() { return ENVS; } diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java new file mode 100644 index 000000000000..16e1cd43afaf --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.SourceScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestClass extends TestNGTestObject { + public TestNGTestClass(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + //it's a class + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); + } + } + ); + if (psiClass == null) { + throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); + } + if (null == ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + public String compute() { + return psiClass.getQualifiedName(); + } + })) { + throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); + } + calculateDependencies(null, classes, psiClass); + } + + @Override + public String getGeneratedName() { + return JavaExecutionUtil.getPresentableClassName(myConfig.getPersistantData().getMainClassName()); + } + + @Override + public String getActionName() { + return JavaExecutionUtil.getShortClassName(myConfig.getPersistantData().MAIN_CLASS_NAME); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + final SourceScope scope = data.getScope().getSourceScope(myConfig); + if (scope == null) { + throw new RuntimeConfigurationException("Invalid scope specified"); + } + PsiClass psiClass = JavaPsiFacade.getInstance(myConfig.getProject()).findClass(data.getMainClassName(), scope.getGlobalSearchScope()); + if (psiClass == null) throw new RuntimeConfigurationException("Class '" + data.getMainClassName() + "' not found"); + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); + if (element instanceof PsiClass) { + return Comparing.strEqual(myConfig.getPersistantData().getMethodName(), JavaExecutionUtil.getRuntimeQualifiedName((PsiClass) element)); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java new file mode 100644 index 000000000000..ef24a830ec65 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class TestNGTestGroup extends TestNGTestObject { + public TestNGTestGroup(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + //for a group, we include all classes + final TestClassFilter classFilter = + new TestClassFilter(data.getScope().getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true); + PsiClass[] testClasses = TestNGUtil.getAllTestClasses(classFilter, false); + if (testClasses != null) { + for (PsiClass c : testClasses) { + classes.put(c, new LinkedHashMap>()); + } + } + } + + @Override + public String getGeneratedName() { + return myConfig.getPersistantData().getGroupName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getGroupName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + //check group exist? + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java new file mode 100644 index 000000000000..16e08f4de0de --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.SourceScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestMethod extends TestNGTestObject { + public TestNGTestMethod(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); + } + } + ); + if (psiClass == null) { + throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); + } + if (null == ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public String compute() { + return psiClass.getQualifiedName(); + } + } + )) { + throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); + } + collectTestMethods(classes, psiClass, data.getMethodName()); + } + + @Override + public String getGeneratedName() { + final TestData data = myConfig.getPersistantData(); + return JavaExecutionUtil.getPresentableClassName(data.getMainClassName()) + '.' + data.getMethodName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getMethodName() + "()"; + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + final SourceScope scope = data.getScope().getSourceScope(myConfig); + if (scope == null) { + throw new RuntimeConfigurationException("Invalid scope specified"); + } + PsiClass psiClass = JavaPsiFacade.getInstance(myConfig.getProject()).findClass(data.getMainClassName(), scope.getGlobalSearchScope()); + if (psiClass == null) throw new RuntimeConfigurationException("Class '" + data.getMainClassName() + "' not found"); + PsiMethod[] methods = psiClass.findMethodsByName(data.getMethodName(), true); + if (methods.length == 0) { + throw new RuntimeConfigurationException("Method '" + data.getMethodName() + "' not found"); + } + for (PsiMethod method : methods) { + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) { + throw new RuntimeConfigurationException("Non public method '" + data.getMethodName() + "'specified"); + } + } + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); + if (element instanceof PsiMethod) { + final PsiClass aClass = ((PsiMethod) element).getContainingClass(); + final TestData data = myConfig.getPersistantData(); + return aClass != null && + Comparing.strEqual(data.MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName(aClass)) && + Comparing.strEqual(data.METHOD_NAME, ((PsiMethod) element).getName()); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java new file mode 100644 index 000000000000..2f839e5aaefb --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java @@ -0,0 +1,247 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.AnnotatedMembersSearch; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public abstract class TestNGTestObject { + private static final Logger LOG = Logger.getInstance("#" + TestNGTestObject.class.getName()); + protected final TestNGConfiguration myConfig; + + public TestNGTestObject(TestNGConfiguration config) { + myConfig = config; + } + + public static TestNGTestObject fromConfig(TestNGConfiguration config) { + final TestData data = config.getPersistantData(); + if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) { + return new TestNGTestPackage(config); + } + if (data.TEST_OBJECT.equals(TestType.CLASS.getType())) { + return new TestNGTestClass(config); + } + if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) { + return new TestNGTestMethod(config); + } + + if (data.TEST_OBJECT.equals(TestType.GROUP.getType())) { + return new TestNGTestGroup(config); + } + + if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) { + return new TestNGTestPattern(config); + } + + if (data.TEST_OBJECT.equals(TestType.SUITE.getType())){ + return new TestNGTestSuite(config); + } + assert false : data.TEST_OBJECT; + return null; + } + + public abstract void fillTestObjects(final Map>> classes) throws CantRunException; + public abstract String getGeneratedName(); + public abstract String getActionName(); + public abstract void checkConfiguration() throws RuntimeConfigurationException; + + public boolean isConfiguredByElement(PsiElement element) { + return false; + } + + protected void calculateDependencies(PsiMethod[] methods, + final Map>> results, + @Nullable final PsiClass... classes) { + calculateDependencies(methods, results, new LinkedHashSet(), classes); + } + + private void calculateDependencies(final PsiMethod[] methods, + final Map>> results, + final Set alreadyMarkedToBeChecked, + @Nullable final PsiClass... classes) { + if (classes != null && classes.length > 0) { + final Set groupDependencies = new LinkedHashSet(); + TestNGUtil.collectAnnotationValues(groupDependencies, "dependsOnGroups", methods, classes); + final Set membersToCheckNow = new LinkedHashSet(); + if (!groupDependencies.isEmpty()) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + final Project project = classes[0].getProject(); + final PsiClass testAnnotation = + JavaPsiFacade.getInstance(project).findClass(TestNGUtil.TEST_ANNOTATION_FQN, GlobalSearchScope.allScope(project)); + LOG.assertTrue(testAnnotation != null); + for (PsiMember psiMember : AnnotatedMembersSearch.search(testAnnotation, getSearchScope())) { + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(psiMember, TestNGUtil.TEST_ANNOTATION_FQN); + if (TestNGUtil.isAnnotatedWithParameter(annotation, "groups", groupDependencies)) { + if (appendMember(psiMember, alreadyMarkedToBeChecked, results)) { + membersToCheckNow.add(psiMember); + } + } + } + } + }); + } + + collectDependsOnMethods(results, alreadyMarkedToBeChecked, membersToCheckNow, methods, classes); + + if (methods == null) { + for (PsiClass c : classes) { + results.put(c, new LinkedHashMap>()); + } + } else { + for (PsiMember psiMember : membersToCheckNow) { + PsiClass psiClass; + PsiMethod[] meths = null; + if (psiMember instanceof PsiMethod) { + psiClass = psiMember.getContainingClass(); + meths = new PsiMethod[] {(PsiMethod)psiMember}; + } else { + psiClass = (PsiClass)psiMember; + } + calculateDependencies(meths, results, alreadyMarkedToBeChecked, psiClass); + } + } + } + } + + private static void collectDependsOnMethods(final Map>> results, + final Set alreadyMarkedToBeChecked, + final Set membersToCheckNow, + final PsiMethod[] methods, + final PsiClass... classes) { + final PsiClass[] psiClasses; + if (methods != null && methods.length > 0) { + final Set containingClasses = new LinkedHashSet(); + for (final PsiMethod method : methods) { + containingClasses.add(ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiClass compute() { + return method.getContainingClass(); + } + })); + } + psiClasses = containingClasses.toArray(new PsiClass[containingClasses.size()]); + } else { + psiClasses = classes; + } + for (final PsiClass containingClass : psiClasses) { + final Set testMethodDependencies = new LinkedHashSet(); + TestNGUtil.collectAnnotationValues(testMethodDependencies, "dependsOnMethods", methods, containingClass); + if (!testMethodDependencies.isEmpty()) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + final Project project = containingClass.getProject(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + for (String dependency : testMethodDependencies) { + final String className = StringUtil.getPackageName(dependency); + final String methodName = StringUtil.getShortName(dependency); + if (StringUtil.isEmpty(className)) { + checkClassMethods(methodName, containingClass, alreadyMarkedToBeChecked, membersToCheckNow, results); + } + else { + final PsiClass aClass = psiFacade.findClass(className, containingClass.getResolveScope()); + if (aClass != null) { + checkClassMethods(methodName, aClass, alreadyMarkedToBeChecked, membersToCheckNow, results); + } + } + } + } + }); + } + } + } + + private static void checkClassMethods(String methodName, + PsiClass containingClass, + Set alreadyMarkedToBeChecked, + Set membersToCheckNow, + Map>> results) { + final PsiMethod[] psiMethods = containingClass.findMethodsByName(methodName, true); + for (PsiMethod method : psiMethods) { + if (AnnotationUtil.isAnnotated(method, TestNGUtil.TEST_ANNOTATION_FQN, false) && + appendMember(method, alreadyMarkedToBeChecked, results)) { + membersToCheckNow.add(method); + } + } + } + + private static boolean appendMember(final PsiMember psiMember, + final Set underConsideration, + final Map>> results) { + boolean result = false; + final PsiClass psiClass = psiMember instanceof PsiClass ? ((PsiClass)psiMember) : psiMember.getContainingClass(); + Map> psiMethods = results.get(psiClass); + if (psiMethods == null) { + psiMethods = new LinkedHashMap>(); + results.put(psiClass, psiMethods); + if (psiMember instanceof PsiClass) { + result = underConsideration.add(psiMember); + } + } + if (psiMember instanceof PsiMethod) { + final boolean add = psiMethods.put((PsiMethod)psiMember, Collections.emptyList()) != null; + if (add) { + return underConsideration.add(psiMember); + } + return false; + } + return result; + } + + @NotNull + protected GlobalSearchScope getSearchScope() { + final TestData data = myConfig.getPersistantData(); + final Module module = myConfig.getConfigurationModule().getModule(); + return data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) + ? myConfig.getPersistantData().getScope().getSourceScope(myConfig).getGlobalSearchScope() + : module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(myConfig.getProject()); + } + + protected void collectTestMethods(Map>> classes, final PsiClass psiClass, final String methodName) { + final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( + new Computable() { + public PsiMethod[] compute() { + return psiClass.findMethodsByName(methodName, true); + } + } + ); + calculateDependencies(methods, classes, psiClass); + Map> psiMethods = classes.get(psiClass); + if (psiMethods == null) { + psiMethods = new LinkedHashMap>(); + classes.put(psiClass, psiMethods); + } + for (PsiMethod method : methods) { + psiMethods.put(method, Collections.emptyList()); + } + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java new file mode 100644 index 000000000000..0a6315b41706 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.search.PackageScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestPackage extends TestNGTestObject { + public TestNGTestPackage(TestNGConfiguration configuration) { + super(configuration); + } + + @Override + public void fillTestObjects(Map>> classes) throws CantRunException { + final String packageName = myConfig.getPersistantData().getPackageName(); + PsiPackage psiPackage = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiPackage compute() { + return JavaPsiFacade.getInstance(myConfig.getProject()).findPackage(packageName); + } + } + ); + if (psiPackage == null) { + throw CantRunException.packageNotFound(packageName); + } + else { + TestSearchScope scope = myConfig.getPersistantData().getScope(); + //TODO we should narrow this down by module really, if that's what's specified + TestClassFilter projectFilter = + new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true); + TestClassFilter filter = projectFilter.intersectionWith(PackageScope.packageScope(psiPackage, true)); + calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(filter, false)); + if (classes.size() == 0) { + throw new CantRunException("No tests found in the package \"" + packageName + '\"'); + } + } + } + + @Override + public String getGeneratedName() { + final String packageName = myConfig.getPersistantData().getPackageName(); + return packageName.length() == 0 ? "" : packageName; + } + + @Override + public String getActionName() { + String s = myConfig.getName(); + if (!myConfig.isGeneratedName()) return '\"' + s + '\"'; + if (myConfig.getPersistantData().getPackageName().trim().length() > 0) { + return "Tests in \"" + myConfig.getPersistantData().getPackageName() + '\"'; + } + else { + return "All Tests"; + } + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + PsiPackage psiPackage = JavaPsiFacade.getInstance(myConfig.getProject()).findPackage(data.getPackageName()); + if (psiPackage == null) throw new RuntimeConfigurationException("Package '" + data.getPackageName() + "' not found"); + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + final String packageName = myConfig.getPersistantData().getPackageName(); + if (element instanceof PsiPackage) { + return Comparing.strEqual(packageName, ((PsiPackage)element).getQualifiedName()); + } else if (element instanceof PsiDirectory) { + final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(((PsiDirectory)element)); + return psiPackage != null && Comparing.strEqual(packageName, psiPackage.getQualifiedName()); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java new file mode 100644 index 000000000000..78e56e27de73 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java @@ -0,0 +1,145 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.configurations.RuntimeConfigurationWarning; +import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.ClassUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class TestNGTestPattern extends TestNGTestObject { + private static final Logger LOG = Logger.getInstance("#" + TestNGTestPattern.class.getName()); + + public TestNGTestPattern(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + for (final String pattern : data.getPatterns()) { + final String className; + final String methodName; + if (pattern.contains(",")) { + methodName = StringUtil.getShortName(pattern, ','); + className = StringUtil.getPackageName(pattern, ','); + } else { + className = pattern; + methodName = null; + } + + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + @Override + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), className.replace('/', '.'), null, true, getSearchScope()); + } + }); + if (psiClass != null) { + final Boolean hasTest = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return TestNGUtil.hasTest(psiClass); + } + }); + if (hasTest) { + if (StringUtil.isEmpty(methodName)) { + calculateDependencies(null, classes, psiClass); + } + else { + collectTestMethods(classes, psiClass, methodName); + } + } else { + throw new CantRunException("No tests found in class " + className); + } + } + } + if (classes.size() != data.getPatterns().size()) { + TestSearchScope scope = myConfig.getPersistantData().getScope(); + final List compilePatterns = new ArrayList(); + for (String p : data.getPatterns()) { + final Pattern compilePattern; + try { + compilePattern = Pattern.compile(p); + } + catch (PatternSyntaxException e) { + continue; + } + if (compilePattern != null) { + compilePatterns.add(compilePattern); + } + } + TestClassFilter projectFilter = + new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true){ + @Override + public boolean isAccepted(PsiClass psiClass) { + if (super.isAccepted(psiClass)) { + final String qualifiedName = psiClass.getQualifiedName(); + LOG.assertTrue(qualifiedName != null); + for (Pattern pattern : compilePatterns) { + if (pattern.matcher(qualifiedName).matches()) return true; + } + } + return false; + } + }; + calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(projectFilter, false)); + if (classes.size() == 0) { + throw new CantRunException("No tests found in for patterns \"" + StringUtil.join(data.getPatterns(), " || ") + '\"'); + } + } + } + + @Override + public String getGeneratedName() { + final Set patterns = myConfig.getPersistantData().getPatterns(); + final int size = patterns.size(); + if (size == 0) return "Temp suite"; + return StringUtil.getShortName(patterns.iterator().next()) + (size > 1 ? " and " + (size - 1) + " more" : ""); + } + + @Override + public String getActionName() { + return getGeneratedName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final Set patterns = myConfig.getPersistantData().getPatterns(); + if (patterns.isEmpty()) { + throw new RuntimeConfigurationWarning("No pattern selected"); + } + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java new file mode 100644 index 000000000000..875891671ccb --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.testng.xml.Parser; + +import java.util.List; +import java.util.Map; + +public class TestNGTestSuite extends TestNGTestObject { + private static final Object PARSE_LOCK = new Object(); + public TestNGTestSuite(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) throws CantRunException {} + + @Override + public String getGeneratedName() { + return myConfig.getPersistantData().getSuiteName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getSuiteName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + try { + final Parser parser = new Parser(data.getSuiteName()); + parser.setLoadClasses(false); + synchronized (PARSE_LOCK) { + parser.parse();//try to parse suite.xml + } + } + catch (Exception e) { + throw new RuntimeConfigurationException("Unable to parse '" + data.getSuiteName() + "' specified"); + } + } +} From c88e351c0e0942801d1755b8dfe3acb3a377dde2 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 12:38:36 +0200 Subject: [PATCH 06/12] module disposed check --- .../impl/src/com/intellij/compiler/impl/CompileDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index a5685017d365..b8c107e3ad35 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -480,7 +480,7 @@ public class CompileDriver { final Set genSourceRoots = new THashSet(FileUtil.FILE_HASHING_STRATEGY); final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); for (Module module : affectedModules) { - if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { + if (!module.isDisposed() && config.getAnnotationProcessingConfiguration(module).isEnabled()) { final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); if (path != null) { genSourceRoots.add(new File(path)); From ff41cf4e51294920eacd13a142c85cb264101f12 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 16 Apr 2015 13:06:40 +0200 Subject: [PATCH 07/12] [performance] avoid extra lexing for lazy parseable elements --- .../intellij/lang/impl/PsiBuilderImpl.java | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index e34406868d95..18888bd1765a 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -133,7 +133,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { this(project, containingFile, parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, charTable, text, - originalTree, parentLightTree, 0); + originalTree, parentLightTree, null); } public PsiBuilderImpl(Project project, @@ -145,7 +145,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final CharSequence text, @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { - this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, 0); + this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, null); } private PsiBuilderImpl(Project project, @@ -157,7 +157,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final CharSequence text, @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree, - int offset) { + LazyParseableToken parentToken) { myProject = project; myFile = containingFile; @@ -170,9 +170,9 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myCharTable = charTable; myOriginalTree = originalTree; myParentLightTree = parentLightTree; - myOffset = offset; + myOffset = parentToken != null ? parentToken.getStartOffset() : 0; - cacheLexemes(); + cacheLexemes(parentToken); } public PsiBuilderImpl(@NotNull final Project project, @@ -190,10 +190,41 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final LighterLazyParseableNode chameleon, @NotNull final CharSequence text) { this(project, chameleon.getContainingFile(), parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, - chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, chameleon.getStartOffset()); + chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, ((LazyParseableToken)chameleon) + ); } - private void cacheLexemes() { + private void cacheLexemes(LazyParseableToken parentToken) { + int[] lexStarts = null; + IElementType[] lexTypes = null; + int lexemeCount = -1; + boolean doLexingOptimizationCorrectionCheck = false; + + if (parentToken != null) { + // there are two types of lazy parseable tokens out there: collapsed out of individual tokens or single token that needs to be expanded + // in first case parent PsiBuilder has all our text lexed so no need to do it again + int tokenCount = parentToken.myEndIndex - parentToken.myStartIndex; + if (tokenCount != 1) { // not expand single lazy parseable token case + lexStarts = new int[tokenCount + 1]; + System.arraycopy(parentToken.myBuilder.myLexStarts, parentToken.myStartIndex, lexStarts, 0, tokenCount); + int diff = parentToken.myBuilder.myLexStarts[parentToken.myStartIndex]; + for(int i = 0; i < tokenCount; ++i) lexStarts[i] -= diff; + lexStarts[tokenCount] = myText.length(); + + lexTypes = new IElementType[tokenCount]; + System.arraycopy(parentToken.myBuilder.myLexTypes, parentToken.myStartIndex, lexTypes, 0, tokenCount); + lexemeCount = tokenCount; + } + ProgressIndicatorProvider.checkCanceled(); + + if (!doLexingOptimizationCorrectionCheck) { + myLexStarts = lexStarts; + myLexTypes = lexTypes; + myLexemeCount = lexemeCount; + return; + } + } + int approxLexCount = Math.max(10, myText.length() / 5); myLexStarts = new int[approxLexCount]; @@ -216,7 +247,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { final IElementType tokenType = myLexer.getTokenType(); sb.append("Token sequence broken") .append("\n this: '").append(myLexer.getTokenText()).append("' (").append(tokenType).append(':') - .append(tokenType != null ? tokenType.getLanguage() : null).append(") ").append(tokenStart).append(":").append(myLexer.getTokenEnd()); + .append(tokenType != null ? tokenType.getLanguage() : null).append(") ").append(tokenStart).append(":") + .append(myLexer.getTokenEnd()); if (i > 0) { final int prevStart = myLexStarts[i - 1]; sb.append("\n prev: '").append(myText.subSequence(prevStart, offset)).append("' (").append(myLexTypes[i - 1]).append(':') @@ -238,6 +270,19 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myLexemeCount = i; clearCachedTokenType(); + + //noinspection ConstantConditions + if (doLexingOptimizationCorrectionCheck) { + if (lexemeCount != -1) { + assert lexemeCount == myLexemeCount; + for(int j = 0; j < lexemeCount; ++j) { + if (myLexStarts[j] != lexStarts[j] || myLexTypes[j] != lexTypes[j]) { + assert false; + } + } + assert myLexStarts[lexemeCount] == lexStarts[lexemeCount]; + } + } } @Override @@ -1215,8 +1260,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { CharSequence context = index < myLexStarts.length ? myText.subSequence(Math.max(0, myLexStarts[index] - 1000), myLexStarts[index]) : ""; String language = myFile != null ? myFile.getLanguage() + ", " : ""; - LOG.error(UNBALANCED_MESSAGE + "\n" + - "language: " + language + "\n" + + LOG.error(UNBALANCED_MESSAGE + "\n" + + "language: " + language + "\n" + "context: '" + context + "'"); } From 8807c84a84c6cc3a7ddea37b6914be05ab5d8f72 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 14 Apr 2015 18:33:41 +0300 Subject: [PATCH 08/12] [git] IDEA-130665 Remember rebase dialog choices --- plugins/git4idea/src/META-INF/plugin.xml | 1 + .../git4idea/config/GitRebaseSettings.java | 89 +++++++++++++++++++ .../src/git4idea/rebase/GitRebaseDialog.java | 64 ++++++++++++- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 plugins/git4idea/src/git4idea/config/GitRebaseSettings.java diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index c08dd9e5ddae..cbe70496408e 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -157,6 +157,7 @@ + { + + private State myState = new State(); + + public static class State { + public boolean INTERACTIVE = true; + public boolean PRESERVE_MERGES = false; + public boolean SHOW_TAGS = false; + public boolean SHOW_REMOTE_BRANCHES = false; + public String ONTO = null; + } + + @Nullable + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public boolean isInteractive() { + return myState.INTERACTIVE; + } + + public void setInteractive(boolean interactive) { + myState.INTERACTIVE = interactive; + } + + public boolean isPreserveMerges() { + return myState.PRESERVE_MERGES; + } + + public void setPreserveMerges(boolean preserveMerges) { + myState.PRESERVE_MERGES = preserveMerges; + } + + public boolean showTags() { + return myState.SHOW_TAGS; + } + + public void setShowTags(boolean showTags) { + myState.SHOW_TAGS = showTags; + } + + public boolean showRemoteBranches() { + return myState.SHOW_REMOTE_BRANCHES; + } + + public void setShowRemoteBranches(boolean showRemoteBranches) { + myState.SHOW_REMOTE_BRANCHES = showRemoteBranches; + } + + @Nullable + public String getOnto() { + return myState.ONTO; + } + + public void setOnto(@Nullable String onto) { + myState.ONTO = onto; + } + +} diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 51c86a93bdbc..9a3119803309 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java @@ -15,10 +15,12 @@ */ package git4idea.rebase; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; @@ -27,12 +29,14 @@ import git4idea.branch.GitBranchUtil; import git4idea.commands.GitCommand; import git4idea.commands.GitLineHandler; import git4idea.config.GitConfigUtil; +import git4idea.config.GitRebaseSettings; import git4idea.i18n.GitBundle; import git4idea.merge.GitMergeUtil; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.ui.GitReferenceValidator; import git4idea.util.GitUIUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -116,7 +120,7 @@ public class GitRebaseDialog extends DialogWrapper { /** * The current branch */ - protected GitBranch myCurrentBranch; + @Nullable protected GitBranch myCurrentBranch; /** * The tags */ @@ -129,6 +133,9 @@ public class GitRebaseDialog extends DialogWrapper { * The validator for from field */ private final GitReferenceValidator myFromValidator; + @NotNull private final GitRebaseSettings mySettings; + + @Nullable private final String myOriginalOntoBranch; /** * A constructor @@ -143,6 +150,7 @@ public class GitRebaseDialog extends DialogWrapper { setOKButtonText(GitBundle.getString("rebase.button")); init(); myProject = project; + mySettings = ServiceManager.getService(myProject, GitRebaseSettings.class); final Runnable validateRunnable = new Runnable() { public void run() { validateFields(); @@ -158,8 +166,18 @@ public class GitRebaseDialog extends DialogWrapper { validateFields(); } }); + setupBranches(); setupStrategy(); + + myInteractiveCheckBox.setSelected(mySettings.isInteractive()); + myPreserveMergesCheckBox.setSelected(mySettings.isPreserveMerges()); + myShowTagsCheckBox.setSelected(mySettings.showTags()); + myShowRemoteBranchesCheckBox.setSelected(mySettings.showRemoteBranches()); + overwriteOntoForCurrentBranch(mySettings); + + myOriginalOntoBranch = GitUIUtil.getTextField(myOntoComboBox).getText(); + validateFields(); } @@ -169,6 +187,29 @@ public class GitRebaseDialog extends DialogWrapper { return myOntoComboBox; } + private void overwriteOntoForCurrentBranch(@NotNull GitRebaseSettings settings) { + String onto = settings.getOnto(); + if (onto != null && !onto.equals(myBranchComboBox.getSelectedItem())) { + if (!isValidRevision(onto)) { + mySettings.setOnto(null); + } + else { + myOntoComboBox.setSelectedItem(onto); + } + } + } + + private boolean isValidRevision(@NotNull String revisionExpression) { + try { + GitRevisionNumber.resolve(myProject, gitRoot(), revisionExpression); + return true; + } + catch (VcsException e) { + LOG.debug(e); + return false; + } + } + public GitLineHandler handler() { GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.REBASE); h.setStdoutSuppressed(false); @@ -202,6 +243,27 @@ public class GitRebaseDialog extends DialogWrapper { return h; } + @Override + protected void doOKAction() { + try { + rememberFields(); + } + finally { + super.doOKAction(); + } + } + + private void rememberFields() { + mySettings.setInteractive(myInteractiveCheckBox.isSelected()); + mySettings.setPreserveMerges(myPreserveMergesCheckBox.isSelected()); + mySettings.setShowTags(myShowTagsCheckBox.isSelected()); + mySettings.setShowRemoteBranches(myShowRemoteBranchesCheckBox.isSelected()); + String onto = StringUtil.nullize(GitUIUtil.getTextField(myOntoComboBox).getText(), true); + if (onto != null && !onto.equals(myOriginalOntoBranch)) { + mySettings.setOnto(onto); + } + } + /** * Setup strategy */ From 0b2e95a23dfd6e9019770b1f94f3e27794caea3a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:46:58 +0300 Subject: [PATCH 09/12] IDEA-136646 IDE should be responsive during global inspections --- .../ex/GlobalInspectionContextImpl.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 8aedfc4f4cae..4e3d7b53feb8 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -325,12 +325,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp appendPairedInspectionsForUnfairTools(globalTools, globalSimpleTools, localTools); ((RefManagerImpl)getRefManager()).initializeAnnotators(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - runGlobalTools(scope, inspectionManager, globalTools); - } - }); + runGlobalTools(scope, inspectionManager, globalTools); if (runGlobalToolsOnly) return; @@ -497,15 +492,16 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp return PsiDocumentManager.getInstance(getProject()).getDocument(file); } - private void runGlobalTools(@NotNull AnalysisScope scope, @NotNull InspectionManager inspectionManager, @NotNull List globalTools) { + private void runGlobalTools(@NotNull final AnalysisScope scope, @NotNull final InspectionManager inspectionManager, @NotNull List globalTools) { + LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed(), "Must not run under read action, too unresponsive"); final List needRepeatSearchRequest = new ArrayList(); - final boolean surelyNoExternalUsages = scope.getScopeType() == AnalysisScope.PROJECT; + final boolean canBeExternalUsages = scope.getScopeType() != AnalysisScope.PROJECT; for (Tools tools : globalTools) { for (ScopeToolState state : tools.getTools()) { - InspectionToolWrapper toolWrapper = state.getTool(); - GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool(); - InspectionToolPresentation toolPresentation = getPresentation(toolWrapper); + final InspectionToolWrapper toolWrapper = state.getTool(); + final GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool(); + final InspectionToolPresentation toolPresentation = getPresentation(toolWrapper); try { if (tool.isGraphNeeded()) { try { @@ -516,11 +512,17 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp throw e; } } - tool.runInspection(scope, inspectionManager, this, toolPresentation); - //skip phase when we are sure that scope already contains everything - if (!surelyNoExternalUsages && tool.queryExternalUsagesRequests(inspectionManager, this, toolPresentation)) { - needRepeatSearchRequest.add(toolWrapper); - } + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + tool.runInspection(scope, inspectionManager, GlobalInspectionContextImpl.this, toolPresentation); + //skip phase when we are sure that scope already contains everything + if (canBeExternalUsages && + tool.queryExternalUsagesRequests(inspectionManager, GlobalInspectionContextImpl.this, toolPresentation)) { + needRepeatSearchRequest.add(toolWrapper); + } + } + }); } catch (ProcessCanceledException e) { throw e; From 3d75cfde13a84113dbe3903a400fcc8da50c1b84 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:48:00 +0300 Subject: [PATCH 10/12] notnull --- .../src/com/intellij/codeInsight/TargetElementUtil.java | 3 ++- .../src/com/intellij/codeInsight/TargetElementUtilBase.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java index 153bafb001f0..c9931b23cbd7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,6 +212,7 @@ public class TargetElementUtil extends TargetElementUtilBase { return null; } + @NotNull @Override public Collection getTargetCandidates(final PsiReference reference) { PsiElement parent = reference.getElement().getParent(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java index b46ea2e52762..7eb959506722 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -336,10 +336,11 @@ public class TargetElementUtilBase { } } + @NotNull public Collection getTargetCandidates(PsiReference reference) { if (reference instanceof PsiPolyVariantReference) { final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false); - final ArrayList navigatableResults = new ArrayList(results.length); + List navigatableResults = new ArrayList(results.length); for(ResolveResult r:results) { PsiElement element = r.getElement(); From 3fd838db562699e3225265568461e7133f473019 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:48:09 +0300 Subject: [PATCH 11/12] IDEA-138236 Multiresolve behaves differently in 14.1 --- .../codeInsight/navigation/CtrlMouseHandler.java | 4 ++-- .../navigation/actions/GotoDeclarationAction.java | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 1d95b055d70b..bb964bbe5795 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -558,7 +558,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } } - final PsiNameIdentifierOwner element = GotoDeclarationAction.findElementToShowUsagesOf(editor, file, offset); + final PsiNameIdentifierOwner element = GotoDeclarationAction.findElementToShowUsagesOf(editor, offset); if (element != null) { PsiElement identifier = element.getNameIdentifier(); return new Info(identifier){ diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java index a633b302c969..a7326672a407 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,8 +83,8 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration"); if (elements.length != 1) { - if (elements.length == 0) { - PsiElement element = findElementToShowUsagesOf(editor, file, editor.getCaretModel().getOffset()); + if (elements.length == 0 && suggestCandidates(TargetElementUtilBase.findReference(editor, offset)).isEmpty()) { + PsiElement element = findElementToShowUsagesOf(editor, editor.getCaretModel().getOffset()); if (element != null) { ShowUsagesAction showUsages = (ShowUsagesAction)ActionManager.getInstance().getAction(ShowUsagesAction.ID); RelativePoint popupPosition = JBPopupFactory.getInstance().guessBestPopupLocation(editor); @@ -111,7 +111,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code } } - public static PsiNameIdentifierOwner findElementToShowUsagesOf(@NotNull Editor editor, @NotNull PsiFile file, int offset) { + public static PsiNameIdentifierOwner findElementToShowUsagesOf(@NotNull Editor editor, int offset) { PsiElement elementAt = TargetElementUtilBase.getInstance().findTargetElement(editor, TargetElementUtilBase.ELEMENT_NAME_ACCEPTED, offset); if (elementAt instanceof PsiNameIdentifierOwner) { return (PsiNameIdentifierOwner)elementAt; @@ -184,7 +184,8 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code return false; } - private static Collection suggestCandidates(final PsiReference reference) { + @NotNull + private static Collection suggestCandidates(@Nullable PsiReference reference) { if (reference == null) { return Collections.emptyList(); } From bb3cd90e3987dcc61fff1c2f1191084023a5537a Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 13:36:43 +0200 Subject: [PATCH 12/12] module disposed check --- .../src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java index 8235094c6e93..d3ece4f553bd 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java @@ -78,7 +78,7 @@ public class CompilerPathsEx extends CompilerPaths { public static String[] getOutputPaths(Module[] modules) { final Set outputPaths = new OrderedSet(); for (Module module : modules) { - final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module); + final CompilerModuleExtension compilerModuleExtension = !module.isDisposed()? CompilerModuleExtension.getInstance(module) : null; if (compilerModuleExtension == null) { continue; }