KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > junit > JUnit3TestGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.junit;
21
22 import com.sun.source.tree.BlockTree;
23 import com.sun.source.tree.ClassTree;
24 import com.sun.source.tree.ExpressionTree;
25 import com.sun.source.tree.MethodInvocationTree;
26 import com.sun.source.tree.MethodTree;
27 import com.sun.source.tree.ModifiersTree;
28 import com.sun.source.tree.ReturnTree;
29 import com.sun.source.tree.StatementTree;
30 import com.sun.source.tree.Tree;
31 import com.sun.source.tree.TypeParameterTree;
32 import com.sun.source.tree.VariableTree;
33 import com.sun.source.util.TreePath;
34 import com.sun.source.util.Trees;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.List JavaDoc;
38 import javax.lang.model.element.Element;
39 import javax.lang.model.element.ExecutableElement;
40 import javax.lang.model.element.Modifier;
41 import javax.lang.model.element.TypeElement;
42 import javax.lang.model.type.TypeKind;
43 import javax.lang.model.type.TypeMirror;
44 import javax.lang.model.util.ElementFilter;
45 import javax.lang.model.util.Elements;
46 import javax.lang.model.util.Types;
47 import org.netbeans.api.java.source.ElementHandle;
48 import org.netbeans.api.java.source.TreeMaker;
49 import org.netbeans.api.java.source.WorkingCopy;
50 import org.openide.ErrorManager;
51 import static javax.lang.model.element.Modifier.ABSTRACT;
52 import static javax.lang.model.element.Modifier.PRIVATE;
53 import static javax.lang.model.element.Modifier.PROTECTED;
54 import static javax.lang.model.element.Modifier.PUBLIC;
55 import static javax.lang.model.element.Modifier.STATIC;
56
57 /**
58  *
59  * @author Marian Petras
60  * @author vstejskal
61  */

62 final class JUnit3TestGenerator extends AbstractTestGenerator {
63     
64     /**
65      */

66     JUnit3TestGenerator(TestGeneratorSetup setup) {
67         super(setup);
68     }
69     
70     /**
71      */

72     JUnit3TestGenerator(TestGeneratorSetup setup,
73                         List JavaDoc<ElementHandle<TypeElement>> srcTopClassHandles,
74                         List JavaDoc<String JavaDoc>suiteMembers,
75                         boolean isNewTestClass) {
76         super(setup, srcTopClassHandles, suiteMembers, isNewTestClass);
77     }
78     
79     
80     /** element representing type {@code junit.framework.Test} */
81     private TypeElement testTypeElem;
82     /** */
83     private TypeElement testCaseTypeElem;
84     /** element representing type {@code junit.framework.TestSuite} */
85     private TypeElement testSuiteTypeElem;
86         
87     
88     /**
89      */

90     protected ClassTree composeNewTestClass(WorkingCopy workingCopy,
91                                             String JavaDoc name,
92                                             List JavaDoc<? extends Tree> members) {
93         final TreeMaker maker = workingCopy.getTreeMaker();
94         ModifiersTree modifiers = maker.Modifiers(
95                                       Collections.<Modifier>singleton(PUBLIC));
96         TypeElement testCaseType = getTestCaseTypeElem(workingCopy.getElements());
97         Tree extendsClause = (testCaseType != null)
98                              ? maker.QualIdent(testCaseType)
99                              : maker.Identifier("junit.framework.TestCase");//NOI18N
100
return maker.Class(
101                     modifiers, //modifiers
102
name, //name
103
Collections.<TypeParameterTree>emptyList(),//type params
104
extendsClause, //extends
105
Collections.<ExpressionTree>emptyList(), //implements
106
members); //members
107
}
108     
109     /**
110      */

111     protected List JavaDoc<? extends Tree> generateInitMembers(WorkingCopy workingCopy) {
112         if (!setup.isGenerateSetUp() && !setup.isGenerateTearDown()) {
113             return Collections.<Tree>emptyList();
114         }
115
116         final TreeMaker maker = workingCopy.getTreeMaker();
117         List JavaDoc<MethodTree> result = new ArrayList JavaDoc<MethodTree>(2);
118         if (setup.isGenerateSetUp()) {
119             result.add(generateInitMethod("setUp", maker)); //NOI18N
120
}
121         if (setup.isGenerateTearDown()) {
122             result.add(generateInitMethod("tearDown", maker)); //NOI18N
123
}
124         return result;
125     }
126
127     /**
128      */

129     protected ClassTree generateMissingInitMembers(ClassTree tstClass,
130                                                    TreePath tstClassTreePath,
131                                                    WorkingCopy workingCopy) {
132         if (!setup.isGenerateSetUp() && !setup.isGenerateTearDown()
133                 && !setup.isGenerateSuiteClasses()) {
134             return tstClass;
135         }
136
137         ClassMap classMap = ClassMap.forClass(tstClass, tstClassTreePath, workingCopy.getTrees());
138
139         if ((!setup.isGenerateSetUp() || classMap.containsSetUp())
140                 && (!setup.isGenerateTearDown() || classMap.containsTearDown())
141                 && (!setup.isGenerateSuiteClasses() || classMap.containsNoArgMethod("suite"))) {//NOI18N
142
return tstClass;
143         }
144
145         List JavaDoc<? extends Tree> tstMembersOrig = tstClass.getMembers();
146         List JavaDoc<Tree> tstMembers = new ArrayList JavaDoc<Tree>(tstMembersOrig.size() + 2);
147         tstMembers.addAll(tstMembersOrig);
148
149         generateMissingInitMembers(tstMembers, classMap, workingCopy);
150         generateTestClassSuiteMethod(tstClassTreePath, tstMembers, classMap,
151                                      workingCopy);
152
153         ClassTree newClass = workingCopy.getTreeMaker().Class(
154                 tstClass.getModifiers(),
155                 tstClass.getSimpleName(),
156                 tstClass.getTypeParameters(),
157                 tstClass.getExtendsClause(),
158                 (List JavaDoc<? extends ExpressionTree>) tstClass.getImplementsClause(),
159                 tstMembers);
160         return newClass;
161     }
162
163     /**
164      */

165     protected boolean generateMissingInitMembers(List JavaDoc<Tree> tstMembers,
166                                                  ClassMap clsMap,
167                                                  WorkingCopy workingCopy) {
168         TreeMaker treeMaker = workingCopy.getTreeMaker();
169         
170         boolean modified = false;
171         if (setup.isGenerateSetUp() && !clsMap.containsSetUp()) {
172             addInitMethod("setUp", //NOI18N
173
clsMap.getTearDownIndex(),
174                           tstMembers,
175                           clsMap,
176                           treeMaker);
177             modified = true;
178         }
179         if (setup.isGenerateTearDown() && !clsMap.containsTearDown()) {
180             int setUpIndex = clsMap.getSetUpIndex();
181             addInitMethod("tearDown", //NOI18N
182
(setUpIndex != -1) ? setUpIndex + 1 : -1,
183                           tstMembers,
184                           clsMap,
185                           treeMaker);
186             modified = true;
187         }
188         return modified;
189     }
190     
191     /**
192      * Creates a new init method ({@code setUp()}, {@code tearDown()}.
193      * When the method is created, it is added to the passed
194      * {@code List<Tree>} of class members and the passed {@code ClassMap}
195      * is updated appropriately.
196      *
197      * @param methodName name of the init method to be added
198      * @param targetIndex position in the list of members where the new
199      * init method should be put; or {@code -1} if this
200      * is the first init method to be added and
201      * the position should be determined automatically
202      * @param clsMembers list of class members to which the created init
203      * method should be added
204      * @param clsMap map of the current class members (will be updated)
205      * @param treeMaker maker to be used for creation of the init method
206      */

207     private void addInitMethod(String JavaDoc methodName,
208                                int targetIndex,
209                                List JavaDoc<Tree> clsMembers,
210                                ClassMap clsMap,
211                                TreeMaker treeMaker) {
212         MethodTree initMethod = generateInitMethod(methodName, treeMaker);
213         
214         if (targetIndex == -1) {
215             targetIndex = getPlaceForFirstInitMethod(clsMap);
216         }
217         
218         if (targetIndex != -1) {
219             clsMembers.add(targetIndex, initMethod);
220         } else {
221             clsMembers.add(initMethod);
222         }
223         clsMap.addNoArgMethod(methodName, targetIndex);
224     }
225
226     /**
227      * Generates a set-up or a tear-down method.
228      * The generated method will have no arguments, void return type
229      * and a declaration that it may throw {@code java.lang.Exception}.
230      * The method will have a declared protected member access.
231      * The method contains call of the corresponding super method, i.e.
232      * {@code super.setUp()} or {@code super.tearDown()}.
233      *
234      * @param methodName name of the method to be created
235      * @return created method
236      * @see http://junit.sourceforge.net/javadoc/junit/framework/TestCase.html
237      * methods {@code setUp()} and {@code tearDown()}
238      */

239     protected MethodTree generateInitMethod(String JavaDoc methodName,
240                                             TreeMaker maker) {
241         ModifiersTree modifiers = maker.Modifiers(
242                 Collections.<Modifier>singleton(PROTECTED));
243         ExpressionTree superMethodCall = maker.MethodInvocation(
244                 Collections.<ExpressionTree>emptyList(), // type params.
245
maker.MemberSelect(
246                         maker.Identifier("super"), methodName), //NOI18N
247
Collections.<ExpressionTree>emptyList());
248         BlockTree methodBody = maker.Block(
249                 Collections.<StatementTree>singletonList(
250                         maker.ExpressionStatement(superMethodCall)),
251                 false);
252         MethodTree method = maker.Method(
253                 modifiers, // modifiers
254
methodName, // name
255
maker.PrimitiveType(TypeKind.VOID), // return type
256
Collections.<TypeParameterTree>emptyList(), // type params
257
Collections.<VariableTree>emptyList(), // parameters
258
Collections.<ExpressionTree>singletonList(
259                         maker.Identifier("Exception")), // throws...//NOI18N
260
methodBody,
261                 null); // default value
262
return method;
263     }
264
265     /**
266      */

267     protected void generateMissingPostInitMethods(TreePath tstClassTreePath,
268                                                   List JavaDoc<Tree> tstMembers,
269                                                   ClassMap clsMap,
270                                                   WorkingCopy workingCopy) {
271         if (setup.isGenerateSuiteClasses()) {
272             generateTestClassSuiteMethod(tstClassTreePath,
273                                          tstMembers,
274                                          clsMap,
275                                          workingCopy);
276         }
277     }
278     
279     /**
280      */

281     protected MethodTree composeNewTestMethod(String JavaDoc testMethodName,
282                                               BlockTree testMethodBody,
283                                               List JavaDoc<ExpressionTree> throwsList,
284                                               WorkingCopy workingCopy) {
285         TreeMaker maker = workingCopy.getTreeMaker();
286         return maker.Method(
287                 maker.Modifiers(createModifierSet(PUBLIC)),
288                 testMethodName,
289                 maker.PrimitiveType(TypeKind.VOID),
290                 Collections.<TypeParameterTree>emptyList(),
291                 Collections.<VariableTree>emptyList(),
292                 throwsList,
293                 testMethodBody,
294                 null); //default value - used by annotations
295
}
296
297     /**
298      */

299     protected ClassTree finishSuiteClass(ClassTree tstClass,
300                                          TreePath tstClassTreePath,
301                                          List JavaDoc<Tree> tstMembers,
302                                          List JavaDoc<String JavaDoc> suiteMembers,
303                                          boolean membersChanged,
304                                          ClassMap classMap,
305                                          WorkingCopy workingCopy) {
306         MethodTree suiteMethod = generateSuiteMethod(
307                                         tstClass.getSimpleName().toString(),
308                                         suiteMembers,
309                                         workingCopy);
310         if (suiteMethod != null) {
311             int suiteMethodIndex = classMap.findNoArgMethod("suite"); //NOI18N
312
if (suiteMethodIndex != -1) {
313                 tstMembers.set(suiteMethodIndex, suiteMethod); //replace method
314
} else {
315                 int targetIndex;
316                 if (classMap.containsInitializers()) {
317                     targetIndex = classMap.getLastInitializerIndex() + 1;
318                 } else if (classMap.containsMethods()) {
319                     targetIndex = classMap.getFirstMethodIndex();
320                 } else if (classMap.containsNestedClasses()) {
321                     targetIndex = classMap.getFirstNestedClassIndex();
322                 } else {
323                     targetIndex = classMap.size();
324                 }
325                 if (targetIndex == classMap.size()) {
326                     tstMembers.add(suiteMethod);
327                 } else {
328                     tstMembers.add(targetIndex, suiteMethod);
329                 }
330                 classMap.addNoArgMethod("suite", targetIndex); //NOI18N
331
}
332             membersChanged = true;
333         }
334
335         //PENDING - generating main(String[]) method:
336
//if (generateMainMethod && !TestUtil.hasMainMethod(tstClass)) {
337
// addMainMethod(tstClass);
338
//}
339

340         if (!membersChanged) {
341             return tstClass;
342         }
343
344         return workingCopy.getTreeMaker().Class(
345                 tstClass.getModifiers(),
346                 tstClass.getSimpleName(),
347                 tstClass.getTypeParameters(),
348                 tstClass.getExtendsClause(),
349                 (List JavaDoc<? extends ExpressionTree>) tstClass.getImplementsClause(),
350                 tstMembers);
351     }
352
353     /**
354      *
355      * @return object representing body of the suite() method,
356      * or {@code null} if an error occured while creating the body
357      */

358     private MethodTree generateSuiteMethod(String JavaDoc suiteName,
359                                           List JavaDoc<String JavaDoc> members,
360                                           WorkingCopy workingCopy) {
361         final Types types = workingCopy.getTypes();
362         final Elements elements = workingCopy.getElements();
363         final TreeMaker maker = workingCopy.getTreeMaker();
364
365         TypeElement testSuiteElem = getTestSuiteTypeElem(elements);
366         if (testSuiteElem == null) {
367             return null;
368         }
369
370         TypeElement testTypeElem = getTestTypeElem(elements);
371         if (testTypeElem == null) {
372             return null;
373         }
374         TypeMirror testType = testTypeElem.asType();
375
376         List JavaDoc<StatementTree> bodyContent
377                 = new ArrayList JavaDoc<StatementTree>(members.size() + 2);
378
379         /* TestSuite suite = new TestSuite("ClassName") */
380
381         VariableTree suiteObjInit = maker.Variable(
382                 maker.Modifiers(noModifiers()),
383                 "suite", //NOI18N
384
maker.QualIdent(testSuiteElem),
385                 maker.NewClass(
386                         null, //enclosing instance
387
Collections.<ExpressionTree>emptyList(), //type args
388
maker.QualIdent(testSuiteElem), //class name
389
Collections.singletonList( //params
390
maker.Literal(TestUtil.getSimpleName(suiteName))),
391                         null)); //class body
392

393         bodyContent.add(suiteObjInit);
394
395         for (String JavaDoc className : members) {
396             TypeElement classElem = elements.getTypeElement(className);
397             if ((classElem != null) && containsSuiteMethod(
398                                                 classElem,
399                                                 elements, types,
400                                                 testType)) {
401
402                 /* suite.addTest(ClassName.suite()) */
403
404                 MethodInvocationTree suiteMethodCall, methodCall;
405                 suiteMethodCall = maker.MethodInvocation(
406                         Collections.<ExpressionTree>emptyList(),
407                         maker.MemberSelect(maker.QualIdent(classElem),
408                                            "suite"), //NOI18N
409
Collections.<ExpressionTree>emptyList());
410                 methodCall = maker.MethodInvocation(
411                         Collections.<ExpressionTree>emptyList(),
412                         maker.MemberSelect(maker.Identifier("suite"), //NOI18N
413
"addTest"), //NOI18N
414
Collections.singletonList(suiteMethodCall));
415
416                 bodyContent.add(maker.ExpressionStatement(methodCall));
417             }
418         }
419
420         /* return suite; */
421
422         bodyContent.add(maker.Return(maker.Identifier("suite"))); //NOI18N
423

424
425         return maker.Method(
426                     maker.Modifiers(createModifierSet(PUBLIC, STATIC)),
427                     "suite", //NOI18N
428
maker.QualIdent(testTypeElem), //return type
429
Collections.<TypeParameterTree>emptyList(),//type params
430
Collections.<VariableTree>emptyList(), //params
431
Collections.<ExpressionTree>emptyList(), //throws-list
432
maker.Block(bodyContent, false), //body
433
null); //def. value - only for annotations
434
}
435
436     /**
437      * Finds whether the given {@code TypeElement} or any of its type
438      * ancestor contains an accessible static no-arg method
439      * of the given name.
440      *
441      * @param typeElement {@code TypeElement} to search
442      * @param methodName name of the method to be found
443      * @param elements support instance to be used for the search
444      * @return {@code true} if the given {@code TypeElement} contains,
445      * whether inherited or declared directly,
446      * a static no-argument method of the given name,
447      * {@code false} otherwise
448      */

449     private boolean containsSuiteMethod(TypeElement typeElement,
450                                         Elements elements,
451                                         Types types,
452                                         TypeMirror testType) {
453         List JavaDoc<ExecutableElement> allMethods
454                 = ElementFilter.methodsIn(elements.getAllMembers(typeElement));
455         for (ExecutableElement method : allMethods) {
456             if (method.getSimpleName().contentEquals("suite") //NOI18N
457
&& method.getParameters().isEmpty()) {
458                 return method.getModifiers().contains(Modifier.STATIC)
459                        && types.isSameType(method.getReturnType(),
460                                            testType);
461             }
462         }
463         return false;
464     }
465
466     /**
467      */

468     private boolean generateTestClassSuiteMethod(TreePath tstClassTreePath,
469                                                    List JavaDoc<Tree> tstMembers,
470                                                    ClassMap clsMap,
471                                                    WorkingCopy workingCopy) {
472         if (!setup.isGenerateSuiteClasses()
473                         || clsMap.containsNoArgMethod("suite")) { //NOI18N
474
return false;
475         }
476
477         final TreeMaker maker = workingCopy.getTreeMaker();
478         final Elements elements = workingCopy.getElements();
479         final Trees trees = workingCopy.getTrees();
480
481         Element tstClassElem = trees.getElement(tstClassTreePath);
482         assert tstClassElem != null;
483
484         List JavaDoc<StatementTree> bodyContent = new ArrayList JavaDoc<StatementTree>(4);
485
486
487         /* TestSuite suite = new TestSuite(MyTestClass.class); */
488
489         VariableTree suiteVar = maker.Variable(
490                 maker.Modifiers(noModifiers()),
491                 "suite", //NOI18N
492
maker.QualIdent(getTestSuiteTypeElem(elements)),
493                 maker.NewClass(
494                         null, //enclosing instance
495
Collections.<ExpressionTree>emptyList(),
496                         maker.QualIdent(getTestSuiteTypeElem(elements)),
497                         Collections.singletonList(
498                                 maker.MemberSelect(maker.QualIdent(tstClassElem),
499                                                    "class")), //NOI18N
500
null)); //class definition
501

502         bodyContent.add(suiteVar);
503
504         /* suite.addTest(NestedClass.suite()); */
505         /* suite.addTest(AnotherNestedClass.suite()); */
506         /* ... */
507
508         List JavaDoc<TypeElement> nestedClassElems
509                 = ElementFilter.typesIn(tstClassElem.getEnclosedElements());
510         if (!nestedClassElems.isEmpty()) {
511             for (TypeElement nestedClassElem : nestedClassElems) {
512                 if (TestUtil.isClassTest(workingCopy, nestedClassElem)) {
513
514                     /* suite.addTest(NestedClass.suite()); */
515
516                     /* NestedClass.suite() */
517                     MethodInvocationTree arg = maker.MethodInvocation(
518                             Collections.<ExpressionTree>emptyList(),
519                             maker.MemberSelect(
520                                     maker.QualIdent(nestedClassElem),
521                                     "suite"), //NOI18N
522
Collections.<ExpressionTree>emptyList());
523
524                     /* suite.addTest(...) */
525                     MethodInvocationTree methodCall = maker.MethodInvocation(
526                             Collections.<ExpressionTree>emptyList(),
527                             maker.MemberSelect(
528                                     maker.Identifier("suite"), //NOI18N
529
"addTest"), //NOI18N
530
Collections.singletonList(arg));
531
532                     bodyContent.add(maker.ExpressionStatement(methodCall));
533                 }
534             }
535         }
536
537         /* return suite; */
538
539         ReturnTree returnStmt
540                 = maker.Return(maker.Identifier("suite")); //NOI18N
541
bodyContent.add(returnStmt);
542
543         MethodTree suiteMethod = maker.Method(
544                 maker.Modifiers(createModifierSet(PUBLIC, STATIC)),
545                 "suite", //NOI18N
546
maker.QualIdent(getTestTypeElem(elements)), //ret. type
547
Collections.<TypeParameterTree>emptyList(), //type params
548
Collections.<VariableTree>emptyList(), //parameters
549
Collections.<ExpressionTree>emptyList(), //throws ...
550
maker.Block(bodyContent, false), //body
551
null, //default value
552
(TypeElement) tstClassElem);
553
554         int targetIndex;
555         if (clsMap.containsMethods()) {
556             targetIndex = clsMap.getFirstMethodIndex(); //before methods
557
} else if (clsMap.containsNestedClasses()) {
558             targetIndex = clsMap.getFirstNestedClassIndex(); //before nested
559
} else {
560             targetIndex = clsMap.size(); //end of the class
561
}
562
563         if (targetIndex == clsMap.size()) {
564             tstMembers.add(suiteMethod);
565         } else {
566             tstMembers.add(targetIndex, suiteMethod);
567         }
568         clsMap.addNoArgMethod("suite", targetIndex); //NOI18N
569

570         return true;
571     }
572     
573     /**
574      */

575     private TypeElement getTestTypeElem(Elements elements) {
576         if (testTypeElem == null) {
577             testTypeElem = getElemForClassName(
578                                         "junit.framework.Test", //NOI18N
579
elements);
580         }
581         return testTypeElem;
582     }
583
584     /**
585      */

586     private TypeElement getTestCaseTypeElem(Elements elements) {
587         if (testCaseTypeElem == null) {
588             testCaseTypeElem = getElemForClassName(
589                                         "junit.framework.TestCase", //NOI18N
590
elements);
591         }
592         return testCaseTypeElem;
593     }
594
595     /**
596      */

597     private TypeElement getTestSuiteTypeElem(Elements elements) {
598         if (testSuiteTypeElem == null) {
599             testSuiteTypeElem = getElemForClassName(
600                                         "junit.framework.TestSuite",//NOI18N
601
elements);
602         }
603         return testSuiteTypeElem;
604     }
605
606     /**
607      */

608     private static TypeElement getElemForClassName(String JavaDoc className,
609                                             Elements elements) {
610         TypeElement elem = elements.getTypeElement(className);
611         if (elem == null) {
612             ErrorManager.getDefault().log(
613                     ErrorManager.ERROR,
614                     "Could not find TypeElement for " + className); //NOI18N
615
}
616         return elem;
617     }
618
619 }
620
Popular Tags