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)); 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; } 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/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 + "'"); } 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(); 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(); } 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; 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 @@ + 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/config/GitRebaseSettings.java b/plugins/git4idea/src/git4idea/config/GitRebaseSettings.java new file mode 100644 index 000000000000..c1e498957c62 --- /dev/null +++ b/plugins/git4idea/src/git4idea/config/GitRebaseSettings.java @@ -0,0 +1,89 @@ +/* + * 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 git4idea.config; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import org.jetbrains.annotations.Nullable; + +@State(name = "Git.Rebase.Settings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) +public class GitRebaseSettings implements PersistentStateComponent { + + 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/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. diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 649c25486ce6..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,15 @@ 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.*; import javax.swing.event.DocumentEvent; @@ -115,7 +120,7 @@ public class GitRebaseDialog extends DialogWrapper { /** * The current branch */ - protected GitBranch myCurrentBranch; + @Nullable protected GitBranch myCurrentBranch; /** * The tags */ @@ -128,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 @@ -142,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(); @@ -157,11 +166,50 @@ 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(); } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + 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); @@ -195,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 */ 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); 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"); + } + } +} 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());