2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.testIntegration
.createTest
;
18 import com
.intellij
.codeInsight
.CodeInsightBundle
;
19 import com
.intellij
.codeInsight
.CodeInsightUtil
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightNamesUtil
;
21 import com
.intellij
.codeInsight
.intention
.PsiElementBaseIntentionAction
;
22 import com
.intellij
.openapi
.application
.ApplicationManager
;
23 import com
.intellij
.openapi
.editor
.Editor
;
24 import com
.intellij
.openapi
.extensions
.Extensions
;
25 import com
.intellij
.openapi
.fileEditor
.ex
.IdeDocumentHistory
;
26 import com
.intellij
.openapi
.module
.Module
;
27 import com
.intellij
.openapi
.module
.ModuleUtil
;
28 import com
.intellij
.openapi
.project
.Project
;
29 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
30 import com
.intellij
.openapi
.ui
.Messages
;
31 import com
.intellij
.openapi
.util
.TextRange
;
32 import com
.intellij
.openapi
.vfs
.VirtualFile
;
33 import com
.intellij
.psi
.*;
34 import com
.intellij
.psi
.impl
.source
.PostprocessReformattingAspect
;
35 import com
.intellij
.psi
.search
.GlobalSearchScope
;
36 import com
.intellij
.psi
.util
.PsiTreeUtil
;
37 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfo
;
38 import com
.intellij
.testIntegration
.TestFrameworkDescriptor
;
39 import com
.intellij
.testIntegration
.TestIntegrationUtils
;
40 import com
.intellij
.util
.IncorrectOperationException
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
44 import java
.util
.Collection
;
46 public class CreateTestAction
extends PsiElementBaseIntentionAction
{
48 public String
getText() {
49 return CodeInsightBundle
.message("intention.create.test");
53 public String
getFamilyName() {
57 public boolean isAvailable(@NotNull Project project
, Editor editor
, @Nullable PsiElement element
) {
58 if (element
== null) return false;
59 if (Extensions
.getExtensions(TestFrameworkDescriptor
.EXTENSION_NAME
).length
== 0) return false;
61 if (!isAvailableForElement(element
)) return false;
63 PsiClass psiClass
= getContainingClass(element
);
65 PsiJavaToken leftBrace
= psiClass
.getLBrace();
66 if (leftBrace
== null) return false;
67 if (element
.getTextOffset() >= leftBrace
.getTextOffset()) return false;
69 TextRange declarationRange
= HighlightNamesUtil
.getClassDeclarationTextRange(psiClass
);
70 if (!declarationRange
.contains(element
.getTextRange())) return false;
75 public boolean isAvailableForElement(PsiElement element
) {
76 if (element
== null) return false;
78 PsiClass psiClass
= getContainingClass(element
);
80 if (psiClass
== null) return false;
82 Module srcModule
= ModuleUtil
.findModuleForPsiElement(psiClass
);
83 if (srcModule
== null) return false;
85 if (psiClass
.isAnnotationType() ||
86 psiClass
.isInterface() ||
88 psiClass
instanceof PsiAnonymousClass
||
89 PsiTreeUtil
.getParentOfType(psiClass
, PsiClass
.class) != null || // inner
90 isUnderTestSources(psiClass
)) {
96 private boolean isUnderTestSources(PsiClass c
) {
97 ProjectRootManager rm
= ProjectRootManager
.getInstance(c
.getProject());
98 VirtualFile f
= c
.getContainingFile().getVirtualFile();
99 if (f
== null) return false;
100 return rm
.getFileIndex().isInTestSourceContent(f
);
103 public void invoke(@NotNull final Project project
, final Editor editor
, PsiFile file
) throws IncorrectOperationException
{
104 PsiElement element
= file
.findElementAt(editor
.getCaretModel().getOffset());
106 final Module srcModule
= ModuleUtil
.findModuleForPsiElement(file
);
107 final PsiClass srcClass
= getContainingClass(element
);
109 if (srcClass
== null) return;
111 PsiDirectory srcDir
= file
.getContainingDirectory();
112 PsiPackage srcPackage
= JavaDirectoryService
.getInstance().getPackage(srcDir
);
114 final CreateTestDialog d
= new CreateTestDialog(project
,
120 if (!d
.isOK()) return;
122 PostprocessReformattingAspect
.getInstance(project
).postponeFormattingInside(new Runnable() {
124 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
127 IdeDocumentHistory
.getInstance(project
).includeCurrentPlaceAsChangePlace();
129 PsiClass targetClass
= JavaDirectoryService
.getInstance().createClass(d
.getTargetDirectory(), d
.getClassName());
130 addSuperClass(targetClass
, project
, d
.getSuperClassName());
131 Editor editor
= CodeInsightUtil
.positionCursor(project
, targetClass
.getContainingFile(), targetClass
.getLBrace());
133 addTestMethods(editor
,
135 d
.getSelectedTestFrameworkDescriptor(),
136 d
.getSelectedMethods(),
137 d
.shouldGeneratedBefore(),
138 d
.shouldGeneratedAfter());
140 catch (IncorrectOperationException e
) {
141 showErrorLater(project
, d
.getClassName());
149 private void showErrorLater(final Project project
, final String targetClassName
) {
150 ApplicationManager
.getApplication().invokeLater(new Runnable() {
152 Messages
.showErrorDialog(project
,
153 CodeInsightBundle
.message("intention.error.cannot.create.class.message", targetClassName
),
154 CodeInsightBundle
.message("intention.error.cannot.create.class.title"));
159 private void addSuperClass(PsiClass targetClass
, Project project
, String superClassName
) throws IncorrectOperationException
{
160 if (superClassName
== null) return;
162 PsiElementFactory ef
= JavaPsiFacade
.getInstance(project
).getElementFactory();
163 PsiJavaCodeReferenceElement superClassRef
;
165 PsiClass superClass
= findClass(project
, superClassName
);
166 if (superClass
!= null) {
167 superClassRef
= ef
.createClassReferenceElement(superClass
);
170 superClassRef
= ef
.createFQClassNameReferenceElement(superClassName
, GlobalSearchScope
.allScope(project
));
172 targetClass
.getExtendsList().add(superClassRef
);
175 private PsiClass
findClass(Project project
, String fqName
) {
176 GlobalSearchScope scope
= GlobalSearchScope
.allScope(project
);
177 return JavaPsiFacade
.getInstance(project
).findClass(fqName
, scope
);
180 private void addTestMethods(Editor editor
,
181 PsiClass targetClass
,
182 TestFrameworkDescriptor descriptor
,
183 Collection
<MemberInfo
> methods
,
184 boolean generateBefore
,
185 boolean generateAfter
) throws IncorrectOperationException
{
186 if (generateBefore
) {
187 generateMethod(TestIntegrationUtils
.MethodKind
.SET_UP
, descriptor
, targetClass
, editor
, "setUp");
190 generateMethod(TestIntegrationUtils
.MethodKind
.TEAR_DOWN
, descriptor
, targetClass
, editor
, "tearUp");
192 for (MemberInfo m
: methods
) {
193 generateMethod(TestIntegrationUtils
.MethodKind
.TEST
, descriptor
, targetClass
, editor
, m
.getMember().getName());
197 private void generateMethod(TestIntegrationUtils
.MethodKind methodKind
, TestFrameworkDescriptor descriptor
, PsiClass targetClass
, Editor editor
, String name
) {
198 PsiMethod method
= (PsiMethod
)targetClass
.add(TestIntegrationUtils
.createDummyMethod(targetClass
.getProject()));
199 PsiDocumentManager
.getInstance(targetClass
.getProject()).doPostponedOperationsAndUnblockDocument(editor
.getDocument());
200 TestIntegrationUtils
.runTestMethodTemplate(methodKind
, descriptor
, editor
, targetClass
, method
, name
, true);
203 private PsiClass
getContainingClass(PsiElement element
) {
204 return PsiTreeUtil
.getParentOfType(element
, PsiClass
.class, false);
207 public boolean startInWriteAction() {