1 19 20 package org.netbeans.modules.junit; 21 22 import com.sun.source.tree.AnnotationTree; 23 import com.sun.source.tree.BlockTree; 24 import com.sun.source.tree.ClassTree; 25 import com.sun.source.tree.ExpressionTree; 26 import com.sun.source.tree.MethodTree; 27 import com.sun.source.tree.ModifiersTree; 28 import com.sun.source.tree.StatementTree; 29 import com.sun.source.tree.Tree; 30 import com.sun.source.tree.TypeParameterTree; 31 import com.sun.source.tree.VariableTree; 32 import com.sun.source.util.TreePath; 33 import com.sun.source.util.Trees; 34 import java.util.ArrayList ; 35 import java.util.Collections ; 36 import java.util.EnumSet ; 37 import java.util.HashMap ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 import java.util.Map ; 41 import java.util.Set ; 42 import javax.lang.model.element.AnnotationMirror; 43 import javax.lang.model.element.AnnotationValue; 44 import javax.lang.model.element.Element; 45 import javax.lang.model.element.ElementKind; 46 import javax.lang.model.element.ExecutableElement; 47 import javax.lang.model.element.Modifier; 48 import javax.lang.model.element.Name; 49 import javax.lang.model.element.TypeElement; 50 import javax.lang.model.type.TypeKind; 51 import javax.lang.model.type.TypeMirror; 52 import javax.lang.model.util.Elements; 53 import javax.lang.model.util.Types; 54 import org.netbeans.api.java.source.ElementHandle; 55 import org.netbeans.api.java.source.TreeMaker; 56 import org.netbeans.api.java.source.WorkingCopy; 57 import org.openide.ErrorManager; 58 import static javax.lang.model.element.Modifier.PUBLIC; 59 import static javax.lang.model.element.Modifier.PROTECTED; 60 import static javax.lang.model.element.Modifier.PRIVATE; 61 import static javax.lang.model.element.Modifier.STATIC; 62 63 67 public class JUnit4TestGenerator extends AbstractTestGenerator { 68 69 70 static final String ANN_BEFORE_CLASS = "org.junit.BeforeClass"; 72 static final String ANN_AFTER_CLASS = "org.junit.AfterClass"; 74 static final String ANN_BEFORE = "org.junit.Before"; 76 static final String ANN_AFTER = "org.junit.After"; 78 static final String ANN_TEST = "org.junit.Test"; 80 private static final String ANN_RUN_WITH = "org.junit.runner.RunWith"; 82 private static final String ANN_SUITE = "org.junit.runners.Suite"; 84 private static final String ANN_SUITE_MEMBERS = "SuiteClasses"; 86 private static final String BEFORE_CLASS_METHOD_NAME = "setUpClass"; 88 private static final String AFTER_CLASS_METHOD_NAME = "tearDownClass"; 90 private static final String BEFORE_METHOD_NAME = "setUp"; 92 private static final String AFTER_METHOD_NAME = "tearDown"; 94 96 JUnit4TestGenerator(TestGeneratorSetup setup) { 97 super(setup); 98 } 99 100 102 JUnit4TestGenerator(TestGeneratorSetup setup, 103 List <ElementHandle<TypeElement>> srcTopClassHandles, 104 List <String >suiteMembers, 105 boolean isNewTestClass) { 106 super(setup, srcTopClassHandles, suiteMembers, isNewTestClass); 107 } 108 109 110 112 protected ClassTree composeNewTestClass(WorkingCopy workingCopy, 113 String name, 114 List <? extends Tree> members) { 115 final TreeMaker maker = workingCopy.getTreeMaker(); 116 ModifiersTree modifiers = maker.Modifiers( 117 Collections.<Modifier>singleton(PUBLIC)); 118 return maker.Class( 119 modifiers, name, Collections.<TypeParameterTree>emptyList(), null, Collections.<ExpressionTree>emptyList(), members); } 126 127 129 protected List <? extends Tree> generateInitMembers(WorkingCopy workingCopy) { 130 if (!setup.isGenerateBefore() && !setup.isGenerateAfter() 131 && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) { 132 return Collections.<Tree>emptyList(); 133 } 134 135 List <MethodTree> result = new ArrayList <MethodTree>(4); 136 if (setup.isGenerateBeforeClass()) { 137 result.add( 138 generateInitMethod(BEFORE_CLASS_METHOD_NAME, ANN_BEFORE_CLASS, true, workingCopy)); 139 } 140 if (setup.isGenerateAfterClass()) { 141 result.add( 142 generateInitMethod(AFTER_CLASS_METHOD_NAME, ANN_AFTER_CLASS, true, workingCopy)); 143 } 144 if (setup.isGenerateBefore()) { 145 result.add( 146 generateInitMethod(BEFORE_METHOD_NAME, ANN_BEFORE, false, workingCopy)); 147 } 148 if (setup.isGenerateAfter()) { 149 result.add( 150 generateInitMethod(AFTER_METHOD_NAME, ANN_AFTER, false, workingCopy)); 151 } 152 return result; 153 } 154 155 157 protected ClassTree generateMissingInitMembers(ClassTree tstClass, 158 TreePath tstClassTreePath, 159 WorkingCopy workingCopy) { 160 if (!setup.isGenerateBefore() && !setup.isGenerateAfter() 161 && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) { 162 return tstClass; 163 } 164 165 ClassMap classMap = ClassMap.forClass(tstClass, tstClassTreePath, 166 workingCopy.getTrees()); 167 168 if ((!setup.isGenerateBefore() || classMap.containsBefore()) 169 && (!setup.isGenerateAfter() || classMap.containsAfter()) 170 && (!setup.isGenerateBeforeClass() || classMap.containsBeforeClass()) 171 && (!setup.isGenerateAfterClass() || classMap.containsAfterClass())) { 172 return tstClass; 173 } 174 175 final TreeMaker maker = workingCopy.getTreeMaker(); 176 177 List <? extends Tree> tstMembersOrig = tstClass.getMembers(); 178 List <Tree> tstMembers = new ArrayList <Tree>(tstMembersOrig.size() + 4); 179 tstMembers.addAll(tstMembersOrig); 180 181 generateMissingInitMembers(tstMembers, classMap, workingCopy); 182 183 ClassTree newClass = maker.Class( 184 tstClass.getModifiers(), 185 tstClass.getSimpleName(), 186 tstClass.getTypeParameters(), 187 tstClass.getExtendsClause(), 188 (List <? extends ExpressionTree>) tstClass.getImplementsClause(), 189 tstMembers); 190 return newClass; 191 } 192 193 195 protected boolean generateMissingInitMembers(List <Tree> tstMembers, 196 ClassMap clsMap, 197 WorkingCopy workingCopy) { 198 boolean modified = false; 199 200 if (setup.isGenerateBeforeClass() && !clsMap.containsBeforeClass()) { 201 int targetIndex; 202 if (clsMap.containsAfterClass()) { 203 targetIndex = clsMap.getAfterClassIndex(); 204 } else { 205 int beforeIndex = clsMap.getBeforeIndex(); 206 int afterIndex = clsMap.getAfterIndex(); 207 if ((beforeIndex != -1) && (afterIndex != -1)) { 208 targetIndex = Math.min(beforeIndex, afterIndex); 209 } else { 210 218 targetIndex = Math.max(beforeIndex, afterIndex); 219 } 220 } 221 addInitMethod(BEFORE_CLASS_METHOD_NAME, 222 ANN_BEFORE_CLASS, 223 true, 224 targetIndex, 225 tstMembers, 226 clsMap, 227 workingCopy); 228 modified = true; 229 } 230 if (setup.isGenerateAfterClass() && !clsMap.containsAfterClass()) { 231 int targetIndex; 232 if (clsMap.containsBeforeClass()) { 233 targetIndex = clsMap.getBeforeClassIndex() + 1; 234 } else { 235 int beforeIndex = clsMap.getBeforeIndex(); 236 int afterIndex = clsMap.getAfterIndex(); 237 if ((beforeIndex != -1) && (afterIndex != -1)) { 238 targetIndex = Math.min(beforeIndex, afterIndex); 239 } else { 240 targetIndex = Math.max(beforeIndex, afterIndex); 241 } 242 } 243 addInitMethod(AFTER_CLASS_METHOD_NAME, 244 ANN_AFTER_CLASS, 245 true, 246 targetIndex, 247 tstMembers, 248 clsMap, 249 workingCopy); 250 modified = true; 251 } 252 if (setup.isGenerateBefore() && !clsMap.containsBefore()) { 253 int targetIndex; 254 if (clsMap.containsAfter()) { 255 targetIndex = clsMap.getAfterIndex(); 256 } else { 257 int beforeClassIndex = clsMap.getBeforeClassIndex(); 258 int afterClassIndex = clsMap.getAfterClassIndex(); 259 260 270 targetIndex = Math.max(beforeClassIndex, afterClassIndex); 271 if (targetIndex != -1) { 272 targetIndex++; 273 } 274 } 275 addInitMethod(BEFORE_METHOD_NAME, 276 ANN_BEFORE, 277 false, 278 targetIndex, 279 tstMembers, 280 clsMap, 281 workingCopy); 282 modified = true; 283 } 284 if (setup.isGenerateAfter() && !clsMap.containsAfter()) { 285 int targetIndex; 286 if (clsMap.containsBefore()) { 287 targetIndex = clsMap.getBeforeIndex() + 1; 288 } else { 289 int beforeClassIndex = clsMap.getBeforeClassIndex(); 290 int afterClassIndex = clsMap.getAfterClassIndex(); 291 targetIndex = Math.max(beforeClassIndex, afterClassIndex); 292 if (targetIndex != -1) { 293 targetIndex++; 294 } 295 } 296 addInitMethod(AFTER_METHOD_NAME, 297 ANN_AFTER, 298 false, 299 targetIndex, 300 tstMembers, 301 clsMap, 302 workingCopy); 303 modified = true; 304 } 305 306 return modified; 307 } 308 309 311 private void addInitMethod(String methodName, 312 String annotationClassName, 313 boolean isStatic, 314 int targetIndex, 315 List <Tree> clsMembers, 316 ClassMap clsMap, 317 WorkingCopy workingCopy) { 318 MethodTree initMethod = generateInitMethod(methodName, 319 annotationClassName, 320 isStatic, 321 workingCopy); 322 323 if (targetIndex == -1) { 324 targetIndex = getPlaceForFirstInitMethod(clsMap); 325 } 326 327 if (targetIndex != -1) { 328 clsMembers.add(targetIndex, initMethod); 329 } else { 330 clsMembers.add(initMethod); 331 } 332 clsMap.addNoArgMethod(methodName, annotationClassName, targetIndex); 333 } 334 335 348 private MethodTree generateInitMethod(String methodName, 349 String annotationClassName, 350 boolean isStatic, 351 WorkingCopy workingCopy) { 352 Set <Modifier> methodModifiers 353 = isStatic ? createModifierSet(PUBLIC, STATIC) 354 : Collections.<Modifier>singleton(PUBLIC); 355 ModifiersTree modifiers = createModifiersTree(annotationClassName, 356 methodModifiers, 357 workingCopy); 358 TreeMaker maker = workingCopy.getTreeMaker(); 359 BlockTree methodBody = maker.Block( 360 Collections.<StatementTree>emptyList(), 361 false); 362 MethodTree method = maker.Method( 363 modifiers, methodName, maker.PrimitiveType(TypeKind.VOID), Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>singletonList( 369 maker.Identifier("Exception")), methodBody, 371 null); return method; 373 } 374 375 377 protected void generateMissingPostInitMethods(TreePath tstClassTreePath, 378 List <Tree> tstMembers, 379 ClassMap clsMap, 380 WorkingCopy workingCopy) { 381 382 } 383 384 386 protected String createTestMethodName(String smName) { 387 return smName; 388 } 389 390 392 protected MethodTree composeNewTestMethod(String testMethodName, 393 BlockTree testMethodBody, 394 List <ExpressionTree> throwsList, 395 WorkingCopy workingCopy) { 396 TreeMaker maker = workingCopy.getTreeMaker(); 397 return maker.Method( 398 createModifiersTree(ANN_TEST, 399 createModifierSet(PUBLIC), 400 workingCopy), 401 testMethodName, 402 maker.PrimitiveType(TypeKind.VOID), 403 Collections.<TypeParameterTree>emptyList(), 404 Collections.<VariableTree>emptyList(), 405 throwsList, 406 testMethodBody, 407 null); } 409 410 412 protected ClassTree finishSuiteClass(ClassTree tstClass, 413 TreePath tstClassTreePath, 414 List <Tree> tstMembers, 415 List <String > suiteMembers, 416 boolean membersChanged, 417 ClassMap classMap, 418 WorkingCopy workingCopy) { 419 420 ModifiersTree currModifiers = tstClass.getModifiers(); 421 ModifiersTree modifiers = fixSuiteClassModifiers(tstClass, 422 tstClassTreePath, 423 currModifiers, 424 suiteMembers, 425 workingCopy); 426 if (!membersChanged) { 427 if (modifiers != currModifiers) { 428 workingCopy.rewrite(currModifiers, modifiers); 429 } 430 return tstClass; 431 } 432 433 return workingCopy.getTreeMaker().Class( 434 modifiers, 435 tstClass.getSimpleName(), 436 tstClass.getTypeParameters(), 437 tstClass.getExtendsClause(), 438 (List <? extends ExpressionTree>) tstClass.getImplementsClause(), 439 tstMembers); 440 } 441 442 460 private ModifiersTree fixSuiteClassModifiers(ClassTree tstClass, 461 TreePath tstClassTreePath, 462 ModifiersTree modifiers, 463 List <String > suiteMembers, 464 WorkingCopy workingCopy) { 465 boolean flagsModified = false; 466 467 Set <Modifier> currFlags = modifiers.getFlags(); 468 Set <Modifier> flags = EnumSet.copyOf(currFlags); 469 flagsModified |= flags.remove(PRIVATE); 470 flagsModified |= flags.remove(PROTECTED); 471 flagsModified |= flags.add(PUBLIC); 472 if (!flagsModified) { 473 flags = currFlags; 474 } 475 476 477 boolean annotationListModified = false; 478 479 List <? extends AnnotationTree> currAnnotations = modifiers.getAnnotations(); 480 List <? extends AnnotationTree> annotations; 481 if (currAnnotations.isEmpty()) { 482 List <AnnotationTree> newAnnotations = new ArrayList <AnnotationTree>(2); 483 newAnnotations.add(createRunWithSuiteAnnotation(workingCopy)); 484 newAnnotations.add(createSuiteAnnotation(suiteMembers, workingCopy)); 485 annotations = newAnnotations; 486 487 annotationListModified = true; 488 } else { 489 Trees trees = workingCopy.getTrees(); 490 Element classElement = trees.getElement(tstClassTreePath); 491 List <? extends AnnotationMirror> annMirrors 492 = classElement.getAnnotationMirrors(); 493 assert annMirrors.size() == currAnnotations.size(); 494 495 496 int index = -1, runWithIndex = -1, suiteClassesIndex = -1; 497 for (AnnotationMirror annMirror : annMirrors) { 498 index++; 499 Element annElement = annMirror.getAnnotationType().asElement(); 500 assert annElement instanceof TypeElement; 501 TypeElement annTypeElem = (TypeElement) annElement; 502 Name annFullName = annTypeElem.getQualifiedName(); 503 504 if ((runWithIndex == -1) && annFullName.contentEquals(ANN_RUN_WITH)) { 505 runWithIndex = index; 506 } else if ((suiteClassesIndex == -1) 507 && annFullName.contentEquals(ANN_SUITE + '.' + ANN_SUITE_MEMBERS)) { 508 suiteClassesIndex = index; 509 } 510 } 511 512 AnnotationTree runWithSuiteAnn; 513 if ((runWithIndex == -1) || !checkRunWithSuiteAnnotation( 514 annMirrors.get(runWithIndex), 515 workingCopy)) { 516 runWithSuiteAnn = createRunWithSuiteAnnotation(workingCopy); 517 } else { 518 runWithSuiteAnn = currAnnotations.get(runWithIndex); 519 } 520 521 AnnotationTree suiteClassesAnn; 522 if ((suiteClassesIndex == -1) || !checkSuiteMembersAnnotation( 523 annMirrors.get(suiteClassesIndex), 524 suiteMembers, 525 workingCopy)) { 526 suiteClassesAnn = createSuiteAnnotation(suiteMembers, workingCopy); 527 } else { 528 suiteClassesAnn = currAnnotations.get(suiteClassesIndex); 529 } 530 531 if ((runWithIndex != -1) && (suiteClassesIndex != -1)) { 532 if (runWithSuiteAnn != currAnnotations.get(runWithIndex)) { 533 workingCopy.rewrite( 534 currAnnotations.get(runWithIndex), 535 runWithSuiteAnn); 536 } 537 if (suiteClassesAnn != currAnnotations.get(suiteClassesIndex)) { 538 workingCopy.rewrite( 539 currAnnotations.get(suiteClassesIndex), 540 suiteClassesAnn); 541 } 542 annotations = currAnnotations; 543 } else { 544 List <AnnotationTree> newAnnotations 545 = new ArrayList <AnnotationTree>(currAnnotations.size() + 2); 546 if ((runWithIndex == -1) && (suiteClassesIndex == -1)) { 547 548 552 newAnnotations.add(runWithSuiteAnn); 553 newAnnotations.add(suiteClassesAnn); 554 if (!currAnnotations.isEmpty()) { 555 newAnnotations.addAll(currAnnotations); 556 } 557 } else { 558 newAnnotations.addAll(currAnnotations); 559 if (runWithIndex == -1) { 560 assert suiteClassesIndex != 1; 561 562 566 newAnnotations.add(suiteClassesIndex, runWithSuiteAnn); 567 } else { 568 assert runWithIndex != -1; 569 570 574 newAnnotations.add(runWithIndex + 1, suiteClassesAnn); 575 } 576 } 577 annotations = newAnnotations; 578 579 annotationListModified = true; 580 } 581 } 582 583 if (!flagsModified && !annotationListModified) { 584 return modifiers; 585 } 586 587 return workingCopy.getTreeMaker().Modifiers(flags, annotations); 588 } 589 590 599 private boolean checkRunWithSuiteAnnotation(AnnotationMirror annMirror, 600 WorkingCopy workingCopy) { 601 Map <? extends ExecutableElement,? extends AnnotationValue> annParams 602 = annMirror.getElementValues(); 603 604 if (annParams.size() != 1) { 605 return false; 606 } 607 608 AnnotationValue annValue = annParams.values().iterator().next(); 609 Name annValueClsName = getAnnotationValueClassName(annValue, 610 workingCopy.getTypes()); 611 return annValueClsName != null 612 ? annValueClsName.contentEquals(ANN_SUITE) 613 : false; 614 } 615 616 628 private boolean checkSuiteMembersAnnotation(AnnotationMirror annMirror, 629 List <String > suiteMembers, 630 WorkingCopy workingCopy) { 631 Map <? extends ExecutableElement,? extends AnnotationValue> annParams 632 = annMirror.getElementValues(); 633 634 if (annParams.size() != 1) { 635 return false; 636 } 637 638 AnnotationValue annValue = annParams.values().iterator().next(); 639 Object value = annValue.getValue(); 640 if (value instanceof java.util.List ) { 641 List <? extends AnnotationValue> items 642 = (List <? extends AnnotationValue>) value; 643 644 if (items.size() != suiteMembers.size()) { 645 return false; 646 } 647 648 Types types = workingCopy.getTypes(); 649 Iterator <String > suiteMembersIt = suiteMembers.iterator(); 650 for (AnnotationValue item : items) { 651 Name suiteMemberName = getAnnotationValueClassName(item, types); 652 if (suiteMemberName == null) { 653 return false; 654 } 655 if (!suiteMemberName.contentEquals(suiteMembersIt.next())) { 656 return false; 657 } 658 } 659 return true; 660 } 661 662 return false; 663 } 664 665 674 private Name getAnnotationValueClassName(AnnotationValue annValue, 675 Types types) { 676 Object value = annValue.getValue(); 677 if (value instanceof TypeMirror) { 678 TypeMirror typeMirror = (TypeMirror) value; 679 Element typeElement = types.asElement(typeMirror); 680 if (typeElement.getKind() == ElementKind.CLASS) { 681 return ((TypeElement) typeElement).getQualifiedName(); 682 } 683 } 684 return null; 685 } 686 687 692 private AnnotationTree createRunWithSuiteAnnotation( 693 WorkingCopy workingCopy) { 694 TreeMaker maker = workingCopy.getTreeMaker(); 695 696 697 return maker.Annotation( 698 getClassIdentifierTree(ANN_RUN_WITH, workingCopy), 699 Collections.<ExpressionTree>singletonList( 700 maker.MemberSelect( 701 getClassIdentifierTree(ANN_SUITE, workingCopy), 702 "class"))); } 704 705 713 private AnnotationTree createSuiteAnnotation(List <String > suiteMembers, 714 WorkingCopy workingCopy) { 715 final TreeMaker maker = workingCopy.getTreeMaker(); 716 717 List <ExpressionTree> suiteMemberExpressions 718 = new ArrayList <ExpressionTree>(suiteMembers.size()); 719 for (String suiteMember : suiteMembers) { 720 suiteMemberExpressions.add( 721 maker.MemberSelect( 722 getClassIdentifierTree(suiteMember, workingCopy), 723 "class")); } 725 726 727 return maker.Annotation( 728 maker.MemberSelect( 729 getClassIdentifierTree(ANN_SUITE, workingCopy), 730 ANN_SUITE_MEMBERS), 731 Collections.singletonList( 732 maker.Assignment( 733 maker.Identifier("value"), maker.NewArray( 735 maker.Identifier("Class"), Collections.<ExpressionTree>emptyList(), 737 suiteMemberExpressions)))); 738 } 739 740 742 private ModifiersTree createModifiersTree(String annotationClassName, 743 Set <Modifier> modifiers, 744 WorkingCopy workingCopy) { 745 TreeMaker maker = workingCopy.getTreeMaker(); 746 AnnotationTree annotation = maker.Annotation( 747 getClassIdentifierTree(annotationClassName, workingCopy), 748 Collections.<ExpressionTree>emptyList()); 749 return maker.Modifiers(modifiers, 750 Collections.<AnnotationTree>singletonList( 751 annotation)); 752 } 753 754 755 private Map <String , ExpressionTree> classIdentifiers; 756 757 759 private ExpressionTree getClassIdentifierTree(String className, 760 WorkingCopy workingCopy) { 761 ExpressionTree classIdentifier; 762 if (classIdentifiers == null) { 763 classIdentifier = null; 764 classIdentifiers = new HashMap <String , ExpressionTree>(13); 765 } else { 766 classIdentifier = classIdentifiers.get(className); 767 } 768 if (classIdentifier == null) { 769 TypeElement typeElement 770 = getElemForClassName(className, workingCopy.getElements()); 771 TreeMaker maker = workingCopy.getTreeMaker(); 772 classIdentifier = (typeElement != null) 773 ? maker.QualIdent(typeElement) 774 : maker.Identifier(className); 775 classIdentifiers.put(className, classIdentifier); 776 } 777 return classIdentifier; 778 } 779 780 782 private static TypeElement getElemForClassName(String className, 783 Elements elements) { 784 TypeElement elem = elements.getTypeElement(className); 785 if (elem == null) { 786 ErrorManager.getDefault().log( 787 ErrorManager.ERROR, 788 "Could not find TypeElement for " + className); } 790 return elem; 791 } 792 793 } 794 | Popular Tags |