0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.junit;
0043:
0044: import com.sun.source.tree.AnnotationTree;
0045: import com.sun.source.tree.BlockTree;
0046: import com.sun.source.tree.ClassTree;
0047: import com.sun.source.tree.CompilationUnitTree;
0048: import com.sun.source.tree.ExpressionStatementTree;
0049: import com.sun.source.tree.ExpressionTree;
0050: import com.sun.source.tree.IdentifierTree;
0051: import com.sun.source.tree.LiteralTree;
0052: import com.sun.source.tree.MethodInvocationTree;
0053: import com.sun.source.tree.MethodTree;
0054: import com.sun.source.tree.ModifiersTree;
0055: import com.sun.source.tree.StatementTree;
0056: import com.sun.source.tree.Tree;
0057: import com.sun.source.tree.TypeParameterTree;
0058: import com.sun.source.tree.VariableTree;
0059: import com.sun.source.util.TreePath;
0060: import java.io.IOException;
0061: import java.util.ArrayList;
0062: import java.util.Collections;
0063: import java.util.EnumSet;
0064: import java.util.HashMap;
0065: import java.util.HashSet;
0066: import java.util.Iterator;
0067: import java.util.List;
0068: import java.util.Map;
0069: import java.util.Set;
0070: import java.util.logging.Level;
0071: import java.util.logging.Logger;
0072: import javax.lang.model.element.Element;
0073: import javax.lang.model.element.ElementKind;
0074: import javax.lang.model.element.ExecutableElement;
0075: import javax.lang.model.element.Modifier;
0076: import javax.lang.model.element.TypeElement;
0077: import javax.lang.model.element.VariableElement;
0078: import javax.lang.model.type.TypeKind;
0079: import javax.lang.model.type.TypeMirror;
0080: import javax.lang.model.util.ElementFilter;
0081: import javax.lang.model.util.Elements;
0082: import javax.lang.model.util.Types;
0083: import org.netbeans.api.java.source.CancellableTask;
0084: import org.netbeans.api.java.source.ClasspathInfo;
0085: import org.netbeans.api.java.source.Comment;
0086: import org.netbeans.api.java.source.CompilationInfo;
0087: import org.netbeans.api.java.source.ElementHandle;
0088: import org.netbeans.api.java.source.JavaSource.Phase;
0089: import org.netbeans.api.java.source.TreeMaker;
0090: import org.netbeans.api.java.source.WorkingCopy;
0091: import org.openide.ErrorManager;
0092: import org.openide.util.NbBundle;
0093: import static org.netbeans.modules.junit.TestCreator.ACCESS_MODIFIERS;
0094:
0095: /**
0096: * A base class for generators of JUnit test classes and test methods.
0097: *
0098: * @author Marian Petras
0099: */
0100: abstract class AbstractTestGenerator implements
0101: CancellableTask<WorkingCopy> {
0102:
0103: /**
0104: * name of the 'instance' variable in the generated test method skeleton
0105: *
0106: * @see #RESULT_VAR_NAME
0107: * @see #EXP_RESULT_VAR_NAME
0108: */
0109: private static final String INSTANCE_VAR_NAME = "instance"; //NOI18N
0110: /**
0111: * name of the 'result' variable in the generated test method skeleton
0112: *
0113: * @see #EXP_RESULT_VAR_NAME
0114: */
0115: private static final String RESULT_VAR_NAME = "result"; //NOI18N
0116: /**
0117: * name of the 'expected result' variable in the generated test method
0118: * skeleton
0119: *
0120: * @see #RESULT_VAR_NAME
0121: */
0122: private static final String EXP_RESULT_VAR_NAME = "expResult"; //NOI18N
0123: /**
0124: * base for artificial names of variables
0125: * (if there is no name to derive from)
0126: */
0127: private static final String ARTIFICAL_VAR_NAME_BASE = "arg"; //NOI18N
0128: /** */
0129: private static final EnumSet<Modifier> NO_MODIFIERS = EnumSet
0130: .noneOf(Modifier.class);
0131:
0132: /**
0133: * Returns {@code EnumSet} of all access modifiers.
0134: *
0135: * @return {@code EnumSet} of all access modifiers;
0136: * it is guaranteed that the returned set always contains
0137: * the same set of {@code Modifier}s, but the returned
0138: * instance may not always be the same
0139: */
0140: protected static EnumSet<Modifier> accessModifiers() {
0141: /*
0142: * An alternative would be to create an instance of
0143: * unmodifiable Set<Modifier> (e.g. Collections.unmodifiableSet(...))
0144: * and always return this instance. But the instance would not be an
0145: * instance of (subclass of) EnumSet which would significantly slow down
0146: * many operations performed on it.
0147: */
0148: return EnumSet.copyOf(ACCESS_MODIFIERS);
0149: }
0150:
0151: /**
0152: * Returns an empty {@code EnumSet} of {@code Modifier}s.
0153: *
0154: * @return empty {@code EnumSet} of all {@code Modifier}s;
0155: * it is guaranteed that the returned set is always empty
0156: * but the returned instance may not always be the same
0157: */
0158: protected static EnumSet<Modifier> noModifiers() {
0159: /*
0160: * An alternative would be to create an instance of
0161: * unmodifiable Set<Modifier> (e.g. Collections.<Modifier>emptySet())
0162: * and always return that instance. But the instance would not be an
0163: * instance of (subclass of) EnumSet which would significantly slow down
0164: * many operations performed on it.
0165: */
0166: return EnumSet.copyOf(NO_MODIFIERS);
0167: }
0168:
0169: /** */
0170: protected final TestGeneratorSetup setup;
0171:
0172: private final List<ElementHandle<TypeElement>> srcTopClassElemHandles;
0173:
0174: private final List<String> suiteMembers;
0175:
0176: private final boolean isNewTestClass;
0177:
0178: private List<String> processedClassNames;
0179:
0180: /**
0181: * cached value of <code>JUnitSettings.getGenerateMainMethodBody()</code>
0182: */
0183: private String initialMainMethodBody;
0184:
0185: private volatile boolean cancelled = false;
0186:
0187: /**
0188: * Used when creating a new empty test class.
0189: */
0190: protected AbstractTestGenerator(TestGeneratorSetup setup) {
0191: this .setup = setup;
0192: this .srcTopClassElemHandles = null;
0193: this .suiteMembers = null;
0194: this .isNewTestClass = true; //value not used
0195: }
0196:
0197: /**
0198: * Used when creating a test class for a given source class
0199: * or when creating a test suite.
0200: */
0201: protected AbstractTestGenerator(TestGeneratorSetup setup,
0202: List<ElementHandle<TypeElement>> srcTopClassHandles,
0203: List<String> suiteMembers, boolean isNewTestClass) {
0204: this .setup = setup;
0205: this .srcTopClassElemHandles = srcTopClassHandles;
0206: this .suiteMembers = suiteMembers;
0207: this .isNewTestClass = isNewTestClass;
0208: }
0209:
0210: /**
0211: */
0212: public void run(WorkingCopy workingCopy) throws IOException {
0213:
0214: workingCopy.toPhase(Phase.ELEMENTS_RESOLVED);
0215:
0216: CompilationUnitTree compUnit = workingCopy.getCompilationUnit();
0217: List<ClassTree> tstTopClasses = TopClassFinder.findTopClasses(
0218: compUnit, workingCopy.getTreeUtilities());
0219: TreePath compUnitPath = new TreePath(compUnit);
0220:
0221: List<TypeElement> srcTopClassElems = resolveHandles(
0222: workingCopy, srcTopClassElemHandles);
0223:
0224: if ((srcTopClassElems != null) && !srcTopClassElems.isEmpty()) {
0225:
0226: final String className = workingCopy.getClasspathInfo()
0227: .getClassPath(ClasspathInfo.PathKind.SOURCE)
0228: .getResourceName(workingCopy.getFileObject(), '.',
0229: false);
0230:
0231: /* Create/update a test class for each testable source class: */
0232: for (TypeElement srcTopClass : srcTopClassElems) {
0233: createOrUpdateTestClass(srcTopClass, tstTopClasses,
0234: className, compUnitPath, workingCopy);
0235: }
0236: } else if (suiteMembers != null) { //test suite
0237: for (ClassTree tstClass : tstTopClasses) {
0238: TreePath tstClassTreePath = new TreePath(compUnitPath,
0239: tstClass);
0240: ClassTree origTstTopClass = tstClass;
0241: ClassTree tstTopClass = generateMissingSuiteClassMembers(
0242: tstClass, tstClassTreePath, suiteMembers,
0243: isNewTestClass, workingCopy);
0244: if (tstTopClass != origTstTopClass) {
0245: workingCopy.rewrite(origTstTopClass, tstTopClass);
0246: }
0247: classProcessed(tstClass);
0248: }
0249: } else if (srcTopClassElems == null) { //new empty test class
0250: for (ClassTree tstClass : tstTopClasses) {
0251: ClassTree origTstTopClass = tstClass;
0252: ClassTree tstTopClass = generateMissingInitMembers(
0253: tstClass, new TreePath(compUnitPath, tstClass),
0254: workingCopy);
0255: if (tstTopClass != origTstTopClass) {
0256: workingCopy.rewrite(origTstTopClass, tstTopClass);
0257: }
0258: }
0259: }
0260: }
0261:
0262: /**
0263: * Creates or updates a test class for a given source class.
0264: *
0265: * @param srcTopClass source class for which a test class should be
0266: * created or updated
0267: * @param tstTopClasses list of top-level classes that are present
0268: * in the test source code
0269: * @param testClassName desired name of the test class corresponding
0270: * to the given source class; if a test class of the
0271: * given name is not found, it is created
0272: * @param compUnitPath tree-path to the compilation unit of the test
0273: * source file
0274: * @param workingCopy working copy of the test file's structure
0275: */
0276: private void createOrUpdateTestClass(TypeElement srcTopClass,
0277: List<ClassTree> tstTopClasses, String testClassName,
0278: TreePath compUnitPath, WorkingCopy workingCopy) {
0279: List<ExecutableElement> srcMethods = findTestableMethods(srcTopClass);
0280: boolean srcHasTestableMethods = !srcMethods.isEmpty();
0281:
0282: final String testClassSimpleName = TestUtil
0283: .getSimpleName(testClassName);
0284:
0285: /* Check whether the corresponding test class already exists: */
0286: ClassTree tstTopClass = null;
0287: for (ClassTree tstClass : tstTopClasses) {
0288: if (tstClass.getSimpleName().contentEquals(
0289: testClassSimpleName)) {
0290: tstTopClass = tstClass; //yes, it exists
0291: break;
0292: }
0293: }
0294:
0295: if (tstTopClass != null) { //if the test class already exists
0296: TreePath tstTopClassTreePath = new TreePath(compUnitPath,
0297: tstTopClass);
0298:
0299: ClassTree origTstTopClass = tstTopClass;
0300: if (srcHasTestableMethods) {
0301: tstTopClass = generateMissingTestMethods(srcTopClass,
0302: srcMethods, tstTopClass, tstTopClassTreePath,
0303: isNewTestClass, workingCopy);
0304: } else if (isNewTestClass) {
0305: tstTopClass = generateMissingInitMembers(tstTopClass,
0306: tstTopClassTreePath, workingCopy);
0307: }
0308: if (tstTopClass != origTstTopClass) {
0309: workingCopy.rewrite(origTstTopClass, tstTopClass);
0310: }
0311: } else { //it does not exist - it must be created
0312: if (srcHasTestableMethods) {
0313: tstTopClass = generateNewTestClass(workingCopy,
0314: testClassSimpleName, srcTopClass, srcMethods);
0315: //PENDING - add the top class to the CompilationUnit
0316:
0317: //PENDING - generate suite method
0318: }
0319: }
0320: }
0321:
0322: /**
0323: * Generates a new test class for the given source class.
0324: *
0325: * @param name name of the class to be created
0326: * @param srcClass source class for which the test class should be created
0327: * @param srcMethods methods inside the source class for which
0328: * corresponding test methods should be created
0329: * @return generated test class
0330: */
0331: private ClassTree generateNewTestClass(WorkingCopy workingCopy,
0332: String name, TypeElement srcClass,
0333: List<ExecutableElement> srcMethods) {
0334: List<MethodTree> testMethods = generateTestMethods(srcClass,
0335: srcMethods, workingCopy);
0336: return composeNewTestClass(workingCopy, name, testMethods);
0337: }
0338:
0339: /**
0340: * Generates a new test class containing the given list of test methods.
0341: *
0342: * @param name desired name of the test class
0343: * @param members desired content of the test class
0344: * @return generated test class
0345: */
0346: protected abstract ClassTree composeNewTestClass(
0347: WorkingCopy workingCopy, String name,
0348: List<? extends Tree> members);
0349:
0350: /**
0351: */
0352: protected abstract List<? extends Tree> generateInitMembers(
0353: WorkingCopy workingCopy);
0354:
0355: /**
0356: * Generates missing set-up and tear-down methods in the given test class.
0357: *
0358: * @param tstClass test class in which the methods should be generated
0359: * @return a class tree with the missing methods added;
0360: * or the passed class tree if no method was missing
0361: */
0362: protected abstract ClassTree generateMissingInitMembers(
0363: ClassTree tstClass, TreePath tstClassTreePath,
0364: WorkingCopy workingCopy);
0365:
0366: /**
0367: * Generates missing set-up and tear-down methods and adds them to the list
0368: * of class members.
0369: *
0370: * @param tstMembers current list of test class members
0371: * - generated members will be added to it
0372: * @param clsMap index of the test class contents
0373: * - it will be updated if some members are added
0374: * to the list
0375: * @return {@code true} if the list of members was modified,
0376: * {@code false} otherwise
0377: */
0378: protected abstract boolean generateMissingInitMembers(
0379: List<Tree> tstMembers, ClassMap clsMap,
0380: WorkingCopy workingCopy);
0381:
0382: /**
0383: * Finds position for the first init method.
0384: *
0385: * @param clsMap index of the test class contents
0386: * @return index where the first init method should be put,
0387: * or {@code -1} if the method should be put to the end
0388: * of the class
0389: */
0390: protected int getPlaceForFirstInitMethod(ClassMap clsMap) {
0391: int targetIndex;
0392: if (clsMap.containsMethods()) {
0393: targetIndex = clsMap.getFirstMethodIndex();
0394: } else if (clsMap.containsInitializers()) {
0395: targetIndex = clsMap.getLastInitializerIndex() + 1;
0396: } else if (clsMap.containsNestedClasses()) {
0397: targetIndex = clsMap.getFirstNestedClassIndex();
0398: } else {
0399: targetIndex = -1; //end of the class
0400: }
0401: return targetIndex;
0402: }
0403:
0404: /**
0405: *
0406: * @param srcMethods methods to create/update tests for
0407: *
0408: */
0409: protected ClassTree generateMissingTestMethods(
0410: TypeElement srcClass, List<ExecutableElement> srcMethods,
0411: ClassTree tstClass, TreePath tstClassTreePath,
0412: boolean generateMissingInitMembers, WorkingCopy workingCopy) {
0413: if (srcMethods.isEmpty()) {
0414: return tstClass;
0415: }
0416:
0417: ClassMap clsMap = ClassMap.forClass(tstClass, tstClassTreePath,
0418: workingCopy.getTrees());
0419:
0420: List<? extends Tree> tstMembersOrig = tstClass.getMembers();
0421: List<Tree> tstMembers = new ArrayList<Tree>(tstMembersOrig
0422: .size() + 4);
0423: tstMembers.addAll(tstMembersOrig);
0424:
0425: if (generateMissingInitMembers) {
0426: generateMissingInitMembers(tstMembers, clsMap, workingCopy);
0427: }
0428: generateMissingPostInitMethods(tstClassTreePath, tstMembers,
0429: clsMap, workingCopy);
0430:
0431: /* Generate test method names: */
0432: TypeElement tstClassElem = (TypeElement) workingCopy.getTrees()
0433: .getElement(tstClassTreePath);
0434: List<String> testMethodNames = TestMethodNameGenerator
0435: .getTestMethodNames(srcMethods, tstClassElem, clsMap
0436: .getNoArgMethods(), workingCopy);
0437:
0438: Iterator<ExecutableElement> srcMethodsIt = srcMethods
0439: .iterator();
0440: Iterator<String> tstMethodNamesIt = testMethodNames.iterator();
0441:
0442: Boolean useNoArgConstrutor = null;
0443: while (srcMethodsIt.hasNext()) {
0444: assert tstMethodNamesIt.hasNext();
0445:
0446: ExecutableElement srcMethod = srcMethodsIt.next();
0447: String testMethodName = tstMethodNamesIt.next();
0448: int testMethodIndex = clsMap
0449: .findNoArgMethod(testMethodName);
0450: if (testMethodIndex != -1) {
0451: continue; //corresponding test method already exists
0452: }
0453:
0454: if (useNoArgConstrutor == null) {
0455: useNoArgConstrutor = Boolean
0456: .valueOf(hasAccessibleNoArgConstructor(srcClass));
0457: }
0458: MethodTree newTestMethod = generateTestMethod(srcClass,
0459: srcMethod, testMethodName, useNoArgConstrutor
0460: .booleanValue(), workingCopy);
0461:
0462: tstMembers.add(newTestMethod);
0463: clsMap.addNoArgMethod(newTestMethod.getName().toString());
0464: }
0465: assert !tstMethodNamesIt.hasNext();
0466:
0467: if (tstMembers.size() == tstMembersOrig.size()) { //no test method added
0468: return tstClass;
0469: }
0470:
0471: ClassTree newClass = workingCopy.getTreeMaker().Class(
0472: tstClass.getModifiers(),
0473: tstClass.getSimpleName(),
0474: tstClass.getTypeParameters(),
0475: tstClass.getExtendsClause(),
0476: (List<? extends ExpressionTree>) tstClass
0477: .getImplementsClause(), tstMembers);
0478: return newClass;
0479: }
0480:
0481: /**
0482: */
0483: protected abstract void generateMissingPostInitMethods(
0484: TreePath tstClassTreePath, List<Tree> tstMembers,
0485: ClassMap clsMap, WorkingCopy workingCopy);
0486:
0487: /**
0488: * Generates test methods for the given source methods.
0489: * The created test methods will be put to a newly created test class.
0490: * The test class does not exist at the moment this method is called.
0491: *
0492: * @param srcClass source class containing the source methods
0493: * @param srcMethods source methods the test methods should be created for
0494: */
0495: private List<MethodTree> generateTestMethods(TypeElement srcClass,
0496: List<ExecutableElement> srcMethods, WorkingCopy workingCopy) {
0497: if (srcMethods.isEmpty()) {
0498: return Collections.<MethodTree> emptyList();
0499: }
0500:
0501: List<String> testMethodNames = TestMethodNameGenerator
0502: .getTestMethodNames(srcMethods, null, null, //reserved
0503: workingCopy);
0504:
0505: Iterator<ExecutableElement> srcMethodsIt = srcMethods
0506: .iterator();
0507: Iterator<String> tstMethodNamesIt = testMethodNames.iterator();
0508:
0509: boolean useNoArgConstrutor = hasAccessibleNoArgConstructor(srcClass);
0510: List<MethodTree> testMethods = new ArrayList<MethodTree>(
0511: srcMethods.size());
0512: while (srcMethodsIt.hasNext()) {
0513: assert tstMethodNamesIt.hasNext();
0514:
0515: ExecutableElement srcMethod = srcMethodsIt.next();
0516: String testMethodName = tstMethodNamesIt.next();
0517:
0518: testMethods.add(generateTestMethod(srcClass, srcMethod,
0519: testMethodName, useNoArgConstrutor, workingCopy));
0520: }
0521: assert !tstMethodNamesIt.hasNext();
0522: return testMethods;
0523: }
0524:
0525: /**
0526: * Generates a test methods for the given source method.
0527: *
0528: * @param srcClass source class - parent of the source method
0529: * @param srcMethod source method for which the test method should be
0530: * created
0531: * @param useNoArgConstrutor whether a no-argument constructor should be
0532: * used in the default test method body;
0533: * it should not be {@code true} unless
0534: * the source class contains an accessible
0535: * no-argument constructor
0536: * @return the generated test method
0537: */
0538: protected MethodTree generateTestMethod(TypeElement srcClass,
0539: ExecutableElement srcMethod, String testMethodName,
0540: boolean useNoArgConstructor, WorkingCopy workingCopy) {
0541: final TreeMaker maker = workingCopy.getTreeMaker();
0542:
0543: List<ExpressionTree> throwsList;
0544: if (throwsNonRuntimeExceptions(workingCopy, srcMethod)) {
0545: throwsList = Collections
0546: .<ExpressionTree> singletonList(maker
0547: .Identifier("Exception")); //NOI18N
0548: } else {
0549: throwsList = Collections.<ExpressionTree> emptyList();
0550: }
0551:
0552: MethodTree method = composeNewTestMethod(testMethodName,
0553: generateTestMethodBody(srcClass, srcMethod,
0554: useNoArgConstructor, workingCopy), throwsList,
0555: workingCopy);
0556:
0557: if (setup.isGenerateMethodJavadoc()) {
0558: String commentText = NbBundle.getMessage(TestCreator.class,
0559: "TestCreator.variantMethods.JavaDoc.comment", //NOI18N
0560: srcMethod.getSimpleName().toString(), srcClass
0561: .getSimpleName().toString());
0562: Comment javadoc = Comment.create(Comment.Style.JAVADOC, -2,
0563: -2, -2, commentText);
0564: maker.addComment(method, javadoc, true);
0565: }
0566:
0567: return method;
0568: }
0569:
0570: /**
0571: */
0572: protected abstract MethodTree composeNewTestMethod(
0573: String testMethodName, BlockTree testMethodBody,
0574: List<ExpressionTree> throwsList, WorkingCopy workingCopy);
0575:
0576: /**
0577: */
0578: private ClassTree generateMissingSuiteClassMembers(
0579: ClassTree tstClass, TreePath tstClassTreePath,
0580: List<String> suiteMembers, boolean isNewTestClass,
0581: WorkingCopy workingCopy) {
0582: final TreeMaker maker = workingCopy.getTreeMaker();
0583:
0584: List<? extends Tree> tstMembersOrig = tstClass.getMembers();
0585: List<Tree> tstMembers = new ArrayList<Tree>(tstMembersOrig
0586: .size() + 2);
0587: tstMembers.addAll(tstMembersOrig);
0588: boolean membersChanged = false;
0589:
0590: ClassMap classMap = ClassMap.forClass(tstClass,
0591: tstClassTreePath, workingCopy.getTrees());
0592:
0593: if (isNewTestClass) {
0594: membersChanged |= generateMissingInitMembers(tstMembers,
0595: classMap, workingCopy);
0596: }
0597:
0598: return finishSuiteClass(tstClass, tstClassTreePath, tstMembers,
0599: suiteMembers, membersChanged, classMap, workingCopy);
0600: }
0601:
0602: /**
0603: */
0604: protected abstract ClassTree finishSuiteClass(ClassTree tstClass,
0605: TreePath tstClassTreePath, List<Tree> tstMembers,
0606: List<String> suiteMembers, boolean membersChanged,
0607: ClassMap classMap, WorkingCopy workingCopy);
0608:
0609: // <editor-fold defaultstate="collapsed" desc=" disabled code ">
0610: // /**
0611: // */
0612: // private void addMainMethod(final ClassTree classTree) {
0613: // MethodTree mainMethod = createMainMethod(maker);
0614: // if (mainMethod != null) {
0615: // maker.addClassMember(classTree, mainMethod);
0616: // }
0617: // }
0618: //
0619: // /**
0620: // */
0621: // private void fillTestClass(JavaClass srcClass, JavaClass tstClass) {
0622: //
0623: // fillGeneral(tstClass);
0624: //
0625: // List innerClasses = TestUtil.filterFeatures(srcClass,
0626: // JavaClass.class);
0627: //
0628: // /* Create test classes for inner classes: */
0629: // for (Iterator i = innerClasses.iterator(); i.hasNext(); ) {
0630: // JavaClass innerCls = (JavaClass) i.next();
0631: //
0632: // if (!isClassTestable(innerCls).isTestable()) {
0633: // continue;
0634: // }
0635: //
0636: // /*
0637: // * Check whether the test class for the inner class exists
0638: // * and create one if it does not exist:
0639: // */
0640: // String innerTestClsName
0641: // = TestUtil.getTestClassName(innerCls.getSimpleName());
0642: // JavaClass innerTestCls
0643: // = TestUtil.getClassBySimpleName(tstClass,
0644: // innerTestClsName);
0645: // if (innerTestCls == null) {
0646: // innerTestCls = tgtPkg.getJavaClass().createJavaClass();
0647: // innerTestCls.setSimpleName(
0648: // tstClass.getName() + '.' + innerTestClsName);
0649: // tstClass.getFeatures().add(innerTestCls);
0650: // }
0651: //
0652: // /* Process the tested inner class: */
0653: // fillTestClass(innerCls, innerTestCls);
0654: //
0655: // /* Make the inner test class testable with JUnit: */
0656: // innerTestCls.setModifiers(innerTestCls.getModifiers() | Modifier.STATIC);
0657: // }
0658: //
0659: // /* Add the suite() method (only if we are supposed to do so): */
0660: // if (generateSuiteClasses && !hasSuiteMethod(tstClass)) {
0661: // tstClass.getFeatures().add(createTestClassSuiteMethod(tstClass));
0662: // }
0663: //
0664: // /* Create missing test methods: */
0665: // List srcMethods = TestUtil.filterFeatures(srcClass, Method.class);
0666: // for (Iterator i = srcMethods.iterator(); i.hasNext(); ) {
0667: // Method sm = (Method) i.next();
0668: // if (isMethodAcceptable(sm) &&
0669: // tstClass.getMethod(createTestMethodName(sm.getName()),
0670: // Collections.EMPTY_LIST,
0671: // false)
0672: // == null) {
0673: // Method tm = createTestMethod(srcClass, sm);
0674: // tstClass.getFeatures().add(tm);
0675: // }
0676: // }
0677: //
0678: // /* Create abstract class implementation: */
0679: // if (!skipAbstractClasses
0680: // && (Modifier.isAbstract(srcClass.getModifiers())
0681: // || srcClass.isInterface())) {
0682: // createAbstractImpl(srcClass, tstClass);
0683: // }
0684: // }
0685: // </editor-fold>
0686:
0687: // <editor-fold defaultstate="collapsed" desc=" disabled code ">
0688: // /**
0689: // */
0690: // private Constructor createTestConstructor(String className) {
0691: // Constructor constr = tgtPkg.getConstructor().createConstructor(
0692: // className, // name
0693: // Collections.EMPTY_LIST, // annotations
0694: // Modifier.PUBLIC, // modifiers
0695: // null, // Javadoc text
0696: // null, // Javadoc - object
0697: // null, // body - object
0698: // "super(testName);\n", // body - text //NOI18N
0699: // Collections.EMPTY_LIST, // type parameters
0700: // createTestConstructorParams(), // parameters
0701: // null); // exception names
0702: // return constr;
0703: // }
0704: //
0705: // /**
0706: // */
0707: // private List/*<Parameter>*/ createTestConstructorParams() {
0708: // Parameter param = tgtPkg.getParameter().createParameter(
0709: // "testName", // parameter name
0710: // Collections.EMPTY_LIST, // annotations
0711: // false, // not final
0712: // TestUtil.getTypeReference( // type
0713: // tgtPkg, "String"), //NOI18N
0714: // 0, // dimCount
0715: // false); // is not var.arg.
0716: // return Collections.singletonList(param);
0717: // }
0718: // </editor-fold>
0719:
0720: /**
0721: * Creates a public static {@code main(String[])} method
0722: * with the body taken from settings.
0723: *
0724: * @param maker {@code TreeMaker} to use for creating the method
0725: * @return created {@code main(...)} method,
0726: * or {@code null} if the method body would be empty
0727: */
0728: private MethodTree createMainMethod(TreeMaker maker) {
0729: String initialMainMethodBody = getInitialMainMethodBody();
0730: if (initialMainMethodBody.length() == 0) {
0731: return null;
0732: }
0733:
0734: ModifiersTree modifiers = maker.Modifiers(createModifierSet(
0735: Modifier.PUBLIC, Modifier.STATIC));
0736: VariableTree param = maker.Variable(maker.Modifiers(Collections
0737: .<Modifier> emptySet()), "argList", //NOI18N
0738: maker.Identifier("String[]"), //NOI18N
0739: null); //initializer - not used in params
0740: MethodTree mainMethod = maker.Method(modifiers, //public static
0741: "main", //method name "main"//NOI18N
0742: maker.PrimitiveType(TypeKind.VOID), //return type "void"
0743: Collections.<TypeParameterTree> emptyList(), //type params
0744: Collections.<VariableTree> singletonList(param), //method param
0745: Collections.<ExpressionTree> emptyList(), //throws-list
0746: '{' + initialMainMethodBody + '}', //body text
0747: null); //only for annotations
0748:
0749: return mainMethod;
0750: }
0751:
0752: // <editor-fold defaultstate="collapsed" desc=" disabled code ">
0753: // /**
0754: // */
0755: // private void createAbstractImpl(JavaClass srcClass,
0756: // JavaClass tstClass) {
0757: // String implClassName = srcClass.getSimpleName() + "Impl"; //NOI18N
0758: // JavaClass innerClass = tstClass.getInnerClass(implClassName, false);
0759: //
0760: // if (innerClass == null) {
0761: // String javadocText =
0762: // generateMethodJavadoc
0763: // ? javadocText = NbBundle.getMessage(
0764: // TestCreator.class,
0765: // "TestCreator.abstracImpl.JavaDoc.comment",//NOI18N
0766: // srcClass.getName())
0767: // : null;
0768: //
0769: // // superclass
0770: // MultipartId supClass
0771: // = tgtPkg.getMultipartId().createMultipartId(
0772: // srcClass.isInner() ? srcClass.getName()
0773: // : srcClass.getSimpleName(),
0774: // null,
0775: // Collections.EMPTY_LIST);
0776: //
0777: // innerClass = tgtPkg.getJavaClass().createJavaClass(
0778: // implClassName, // class name
0779: // Collections.EMPTY_LIST, // annotations
0780: // Modifier.PRIVATE, // modifiers
0781: // javadocText, // Javadoc text
0782: // null, // Javadoc - object
0783: // Collections.EMPTY_LIST, // contents
0784: // null, // super class name
0785: // Collections.EMPTY_LIST, // interface names
0786: // Collections.EMPTY_LIST);// type parameters
0787: //
0788: // if (srcClass.isInterface()) {
0789: // innerClass.getInterfaceNames().add(supClass);
0790: // } else {
0791: // innerClass.setSuperClassName(supClass);
0792: // }
0793: //
0794: // createImpleConstructors(srcClass, innerClass);
0795: // tstClass.getFeatures().add(innerClass);
0796: // }
0797: //
0798: // // created dummy implementation for all abstract methods
0799: // List abstractMethods = TestUtil.collectFeatures(
0800: // srcClass,
0801: // Method.class,
0802: // Modifier.ABSTRACT,
0803: // true);
0804: // for (Iterator i = abstractMethods.iterator(); i.hasNext(); ) {
0805: // Method oldMethod = (Method) i.next();
0806: // if (innerClass.getMethod(
0807: // oldMethod.getName(),
0808: // TestUtil.getParameterTypes(oldMethod.getParameters()),
0809: // false) == null) {
0810: // Method newMethod = createMethodImpl(oldMethod);
0811: // innerClass.getFeatures().add(newMethod);
0812: // }
0813: //
0814: // }
0815: // }
0816: //
0817: // /**
0818: // */
0819: // private void createImpleConstructors(JavaClass srcClass,
0820: // JavaClass tgtClass) {
0821: // List constructors = TestUtil.filterFeatures(srcClass,
0822: // Constructor.class);
0823: // for (Iterator i = constructors.iterator(); i.hasNext(); ) {
0824: // Constructor ctr = (Constructor) i.next();
0825: //
0826: // if (Modifier.isPrivate(ctr.getModifiers())) {
0827: // continue;
0828: // }
0829: //
0830: // Constructor nctr = tgtPkg.getConstructor().createConstructor();
0831: // nctr.setBodyText("super(" //NOI18N
0832: // + getParameterString(ctr.getParameters())
0833: // + ");\n"); //NOI18N
0834: // nctr.getParameters().addAll(
0835: // TestUtil.cloneParams(ctr.getParameters(), tgtPkg));
0836: // tgtClass.getFeatures().add(nctr);
0837: // }
0838: // }
0839: //
0840: // /**
0841: // */
0842: // private Method createMethodImpl(Method origMethod) {
0843: // Method newMethod = tgtPkg.getMethod().createMethod();
0844: //
0845: // newMethod.setName(origMethod.getName());
0846: //
0847: // /* Set modifiers of the method: */
0848: // int mod = origMethod.getModifiers() & ~Modifier.ABSTRACT;
0849: // if (((JavaClass) origMethod.getDeclaringClass()).isInterface()) {
0850: // mod |= Modifier.PUBLIC;
0851: // }
0852: // newMethod.setModifiers(mod);
0853: //
0854: // // prepare the body of method implementation
0855: // StringBuffer body = new StringBuffer(200);
0856: // if (generateSourceCodeHints) {
0857: // body.append(NbBundle.getMessage(
0858: // TestCreator.class,
0859: // "TestCreator.methodImpl.bodyComment")); //NOI18N
0860: // body.append("\n\n"); //NOI18N
0861: // }
0862: //
0863: // newMethod.setType(origMethod.getType());
0864: // Type type = origMethod.getType();
0865: // if (type != null) {
0866: // String value = null;
0867: // if ((type instanceof JavaClass) || (type instanceof Array)) {
0868: // value = "null"; //NOI18N
0869: // } else if (type instanceof PrimitiveType) {
0870: // PrimitiveTypeKindEnum tke = (PrimitiveTypeKindEnum)
0871: // ((PrimitiveType) type).getKind();
0872: // if (tke.equals(PrimitiveTypeKindEnum.BOOLEAN)) {
0873: // value = "false"; //NOI18N
0874: // } else if (!tke.equals(PrimitiveTypeKindEnum.VOID)) {
0875: // value = "0"; //NOI18N
0876: // }
0877: // }
0878: //
0879: // if (value != null) {
0880: // body.append("return ").append(value).append(";\n"); //NOI18N
0881: // }
0882: // }
0883: //
0884: // newMethod.setBodyText(body.toString());
0885: //
0886: // // parameters
0887: // newMethod.getParameters().addAll(
0888: // TestUtil.cloneParams(origMethod.getParameters(), tgtPkg));
0889: //
0890: // return newMethod;
0891: // }
0892: //
0893: // /**
0894: // */
0895: // private String generateJavadoc(JavaClass srcClass, Method srcMethod) {
0896: // return NbBundle.getMessage(
0897: // TestCreator.class,
0898: // "TestCreator.variantMethods.JavaDoc.comment", //NOI18N
0899: // srcMethod.getName(),
0900: // srcClass.getName());
0901: // }
0902: // </editor-fold>
0903:
0904: /**
0905: * Generates a default body of a test method.
0906: *
0907: * @param srcClass class - parent of the tested source method
0908: * @param srcMethod source method which should be tested by the test
0909: * @param useNoArgConstrutor whether a no-argument constructor should be
0910: * used in the body;
0911: * it should not be {@code true} unless
0912: * the source class contains an accessible
0913: * no-argument constructor
0914: */
0915: protected BlockTree generateTestMethodBody(TypeElement srcClass,
0916: ExecutableElement srcMethod, boolean useNoArgConstructor,
0917: WorkingCopy workingCopy) {
0918: TreeMaker maker = workingCopy.getTreeMaker();
0919:
0920: boolean isStatic = srcMethod.getModifiers().contains(
0921: Modifier.STATIC);
0922: List<StatementTree> statements = new ArrayList<StatementTree>(8);
0923:
0924: if (setup.isGenerateDefMethodBody()) {
0925: StatementTree sout = generateSystemOutPrintln(maker,
0926: srcMethod.getSimpleName().toString());
0927: List<VariableTree> paramVariables = generateParamVariables(
0928: maker, srcMethod);
0929: statements.add(sout);
0930: statements.addAll(paramVariables);
0931:
0932: if (!isStatic) {
0933: VariableTree instanceVarInit = maker
0934: .Variable(
0935: maker.Modifiers(Collections
0936: .<Modifier> emptySet()),
0937: INSTANCE_VAR_NAME,
0938: maker.QualIdent(srcClass),
0939: useNoArgConstructor ? generateNoArgConstructorCall(
0940: maker, srcClass)
0941: : maker.Literal(null));
0942: statements.add(instanceVarInit);
0943: }
0944:
0945: MethodInvocationTree methodCall = maker.MethodInvocation(
0946: Collections.<ExpressionTree> emptyList(), //type args.
0947: maker.MemberSelect(isStatic ? maker
0948: .QualIdent(srcClass) : maker
0949: .Identifier(INSTANCE_VAR_NAME), srcMethod
0950: .getSimpleName()), createIdentifiers(maker,
0951: paramVariables));
0952:
0953: TypeMirror retType = srcMethod.getReturnType();
0954: TypeKind retTypeKind = retType.getKind();
0955:
0956: if ((retTypeKind == TypeKind.VOID)
0957: || (retTypeKind == TypeKind.ERROR)) {
0958: StatementTree methodCallStmt = maker
0959: .ExpressionStatement(methodCall);
0960:
0961: statements.add(methodCallStmt);
0962: } else {
0963: Tree retTypeTree = maker.Type(retType);
0964:
0965: VariableTree expectedValue = maker.Variable(maker
0966: .Modifiers(NO_MODIFIERS), EXP_RESULT_VAR_NAME,
0967: retTypeTree, getDefaultValue(maker, retType));
0968: VariableTree actualValue = maker.Variable(maker
0969: .Modifiers(NO_MODIFIERS), RESULT_VAR_NAME,
0970: retTypeTree, methodCall);
0971:
0972: List<ExpressionTree> comparisonArgs = new ArrayList<ExpressionTree>(
0973: 2);
0974: comparisonArgs.add(maker.Identifier(expectedValue
0975: .getName().toString()));
0976: comparisonArgs.add(maker.Identifier(actualValue
0977: .getName().toString()));
0978:
0979: MethodInvocationTree comparison = maker
0980: .MethodInvocation(Collections
0981: .<ExpressionTree> emptyList(), //type args.
0982: maker.Identifier("assertEquals"), //NOI18N
0983: comparisonArgs);
0984: StatementTree comparisonStmt = maker
0985: .ExpressionStatement(comparison);
0986:
0987: statements.add(expectedValue);
0988: statements.add(actualValue);
0989: statements.add(comparisonStmt);
0990: }
0991: }
0992:
0993: //PENDING - source code hints
0994: // if (generateSourceCodeHints) {
0995: // // generate comments to bodies
0996: // if (needsEmptyLine) {
0997: // newBody.append('\n');
0998: // needsEmptyLine = false;
0999: // }
1000: // newBody.append(NbBundle.getMessage(
1001: // TestCreator.class,
1002: // generateDefMethodBody
1003: // ? "TestCreator.variantMethods.defaultComment"//NOI18N
1004: // : "TestCreator.variantMethods.onlyComment")) //NOI18N
1005: // .append('\n');
1006: // }
1007:
1008: if (setup.isGenerateDefMethodBody()) {
1009: String failMsg = NbBundle.getMessage(TestCreator.class,
1010: "TestCreator.variantMethods.defaultFailMsg"); //NOI18N
1011: MethodInvocationTree failMethodCall = maker
1012: .MethodInvocation(
1013: Collections.<ExpressionTree> emptyList(), //type args.
1014: maker.Identifier("fail"), //NOI18N
1015: Collections
1016: .<ExpressionTree> singletonList(maker
1017: .Literal(failMsg)));
1018:
1019: ExpressionStatementTree exprStatement = maker
1020: .ExpressionStatement(failMethodCall);
1021: if (setup.isGenerateMethodBodyComment()) {
1022: Comment comment = Comment
1023: .create(
1024: Comment.Style.LINE,
1025: -2,
1026: -2,
1027: -2,
1028: NbBundle
1029: .getMessage(
1030: AbstractTestGenerator.class,
1031: "TestCreator.variantMethods.defaultComment"));//NOI18N
1032: maker.addComment(exprStatement, comment, true);
1033: }
1034: statements.add(exprStatement);
1035: }
1036:
1037: return maker.Block(statements, false);
1038: }
1039:
1040: /**
1041: */
1042: private StatementTree generateSystemOutPrintln(TreeMaker maker,
1043: String arg) {
1044: MethodInvocationTree methodInvocation = maker.MethodInvocation(
1045: Collections.<ExpressionTree> emptyList(), //type args
1046: maker.MemberSelect(maker.MemberSelect(maker
1047: .Identifier("System"), "out"), "println"),//NOI18N
1048: Collections.<LiteralTree> singletonList(maker
1049: .Literal(arg))); //args.
1050: return maker.ExpressionStatement(methodInvocation);
1051: }
1052:
1053: /**
1054: */
1055: private List<VariableTree> generateParamVariables(TreeMaker maker,
1056: ExecutableElement srcMethod) {
1057: List<? extends VariableElement> params = srcMethod
1058: .getParameters();
1059: if ((params == null) || params.isEmpty()) {
1060: return Collections.<VariableTree> emptyList();
1061: }
1062:
1063: Set<Modifier> noModifiers = Collections.<Modifier> emptySet();
1064: List<VariableTree> paramVariables = new ArrayList<VariableTree>(
1065: params.size());
1066: String[] varNames = getTestSkeletonVarNames(params);
1067: int index = 0;
1068: for (VariableElement param : params) {
1069: TypeMirror paramType = param.asType();
1070: paramVariables.add(maker
1071: .Variable(maker.Modifiers(noModifiers),
1072: varNames[index++], maker.Type(paramType),
1073: getDefaultValue(maker, paramType)));
1074: }
1075: return paramVariables;
1076: }
1077:
1078: /**
1079: */
1080: private List<IdentifierTree> createIdentifiers(TreeMaker maker,
1081: List<VariableTree> variables) {
1082: List<IdentifierTree> identifiers;
1083: if (variables.isEmpty()) {
1084: identifiers = Collections.<IdentifierTree> emptyList();
1085: } else {
1086: identifiers = new ArrayList<IdentifierTree>(variables
1087: .size());
1088: for (VariableTree var : variables) {
1089: identifiers.add(maker.Identifier(var.getName()
1090: .toString()));
1091: }
1092: }
1093: return identifiers;
1094: }
1095:
1096: /**
1097: * Builds list of variable names for use in a test method skeleton.
1098: * By default, names of variables are same as names of tested method's
1099: * declared parameters. There are three variable names reserved
1100: * for variables holding the instance the tested method will be called on,
1101: * the expected result and the actual result returned
1102: * by the tested method. This method resolves a potential conflict
1103: * if some of the tested method's parameter's name is one of these
1104: * reserved names - in this case, the variable name used is a slight
1105: * modification of the declared parameter's name. The method also resolves
1106: * cases that some or all parameters are without name - in this case,
1107: * an arbitrary name is assigned to each of these unnamed parameters.
1108: * The goal is to ensure that all of the used variable names are unique.
1109: *
1110: * @param sourceMethodParams
1111: * list of tested method's parameters (items are of type
1112: * <code>org.netbeans.jmi.javamodel.TypeParameter</code>)
1113: * @return variable names used for default values of the tested method's
1114: * parameters (the reserved variable names are not included)
1115: */
1116: private String[] getTestSkeletonVarNames(
1117: final List<? extends VariableElement> sourceMethodParams) {
1118:
1119: /* Handle the trivial case: */
1120: if (sourceMethodParams.isEmpty()) {
1121: return new String[0];
1122: }
1123:
1124: final int count = sourceMethodParams.size();
1125: String[] varNames = new String[count];
1126: boolean[] conflicts = new boolean[count];
1127: boolean issueFound = false;
1128:
1129: Set<String> varNamesSet = new HashSet<String>(
1130: (int) ((count + 2) * 1.4));
1131: varNamesSet.add(INSTANCE_VAR_NAME);
1132: varNamesSet.add(RESULT_VAR_NAME);
1133: varNamesSet.add(EXP_RESULT_VAR_NAME);
1134:
1135: Iterator<? extends VariableElement> it = sourceMethodParams
1136: .iterator();
1137: for (int i = 0; i < count; i++) {
1138: String paramName = it.next().getSimpleName().toString();
1139: varNames[i] = paramName;
1140:
1141: if (paramName == null) {
1142: issueFound = true;
1143: } else if (!varNamesSet.add(paramName)) {
1144: conflicts[i] = true;
1145: issueFound = true;
1146: } else {
1147: conflicts[i] = false;
1148: }
1149: }
1150:
1151: if (issueFound) {
1152: for (int i = 0; i < count; i++) {
1153: String paramName;
1154: if (varNames[i] == null) {
1155: paramName = ARTIFICAL_VAR_NAME_BASE + i;
1156: if (varNamesSet.add(paramName)) {
1157: varNames[i] = paramName;
1158: continue;
1159: } else {
1160: conflicts[i] = true;
1161: }
1162: }
1163: if (conflicts[i]) {
1164: String paramNamePrefix = varNames[i] + '_';
1165:
1166: int index = 2;
1167: while (!varNamesSet
1168: .add(paramName = (paramNamePrefix + (index++))))
1169: ;
1170: varNames[i] = paramName;
1171: }
1172: }
1173: }
1174:
1175: return varNames;
1176: }
1177:
1178: /**
1179: */
1180: private ExpressionTree getDefaultValue(TreeMaker maker,
1181: TypeMirror type) {
1182: ExpressionTree defValue;
1183: TypeKind typeKind = type.getKind();
1184: if (typeKind.isPrimitive()) {
1185: switch (typeKind) {
1186: case BOOLEAN:
1187: defValue = maker.Literal(Boolean.FALSE);
1188: break;
1189: case CHAR:
1190: defValue = maker.Literal(new Character(' '));
1191: break;
1192: case BYTE:
1193: defValue = maker.Literal(new Byte((byte) 0));
1194: break;
1195: case SHORT:
1196: defValue = maker.Literal(new Short((short) 0));
1197: break;
1198: case INT:
1199: defValue = maker.Literal(new Integer(0));
1200: break;
1201: case FLOAT:
1202: defValue = maker.Literal(new Float(0.0F));
1203: break;
1204: case LONG:
1205: defValue = maker.Literal(new Long(0L));
1206: break;
1207: case DOUBLE:
1208: defValue = maker.Literal(new Double(0.0));
1209: break;
1210: default:
1211: assert false : "unknown primitive type"; //NOI18N
1212: defValue = maker.Literal(new Integer(0));
1213: break;
1214: }
1215: } else if ((typeKind == TypeKind.DECLARED)
1216: && type.toString().equals("java.lang.String")) { //NOI18N
1217: defValue = maker.Literal(""); //NOI18N
1218: } else {
1219: defValue = maker.Literal(null);
1220: }
1221: return defValue;
1222: }
1223:
1224: /**
1225: */
1226: private ExpressionTree generateNoArgConstructorCall(
1227: TreeMaker maker, TypeElement cls) {
1228: return maker.NewClass(null, //enclosing instance
1229: Collections.<ExpressionTree> emptyList(),//type arguments
1230: maker.QualIdent(cls), //class identifier
1231: Collections.<ExpressionTree> emptyList(),//arguments list
1232: null); //class body
1233: }
1234:
1235: /**
1236: */
1237: private List<ExecutableElement> findTestableMethods(
1238: TypeElement classElem) {
1239: List<ExecutableElement> methods = ElementFilter
1240: .methodsIn(classElem.getEnclosedElements());
1241:
1242: if (methods.isEmpty()) {
1243: return Collections.<ExecutableElement> emptyList();
1244: }
1245:
1246: List<ExecutableElement> testableMethods = null;
1247:
1248: int skippedCount = 0;
1249: for (ExecutableElement method : methods) {
1250: if (isTestableMethod(method)) {
1251: if (testableMethods == null) {
1252: testableMethods = new ArrayList<ExecutableElement>(
1253: methods.size() - skippedCount);
1254: }
1255: testableMethods.add(method);
1256: } else {
1257: skippedCount++;
1258: }
1259: }
1260:
1261: return (testableMethods != null) ? testableMethods
1262: : Collections.<ExecutableElement> emptyList();
1263: }
1264:
1265: /**
1266: */
1267: private boolean isTestableMethod(ExecutableElement method) {
1268: if (method.getKind() != ElementKind.METHOD) {
1269: throw new IllegalArgumentException();
1270: }
1271:
1272: return setup.isMethodTestable(method);
1273: }
1274:
1275: /**
1276: */
1277: protected boolean hasAccessibleNoArgConstructor(TypeElement srcClass) {
1278: boolean answer;
1279:
1280: List<ExecutableElement> constructors = ElementFilter
1281: .constructorsIn(srcClass.getEnclosedElements());
1282:
1283: if (constructors.isEmpty()) {
1284: answer = true; //no explicit constructor -> synthetic no-arg. constructor
1285: } else {
1286: answer = false;
1287: for (ExecutableElement constructor : constructors) {
1288: if (constructor.getParameters().isEmpty()) {
1289: answer = !constructor.getModifiers().contains(
1290: Modifier.PRIVATE);
1291: break;
1292: }
1293: }
1294: }
1295: return answer;
1296: }
1297:
1298: /**
1299: */
1300: private boolean throwsNonRuntimeExceptions(
1301: CompilationInfo compInfo, ExecutableElement method) {
1302: List<? extends TypeMirror> thrownTypes = method
1303: .getThrownTypes();
1304: if (thrownTypes.isEmpty()) {
1305: return false;
1306: }
1307:
1308: String runtimeExcName = "java.lang.RuntimeException"; //NOI18N
1309: TypeElement runtimeExcElement = compInfo.getElements()
1310: .getTypeElement(runtimeExcName);
1311: if (runtimeExcElement == null) {
1312: Logger.getLogger("junit").log( //NOI18N
1313: Level.WARNING, "Could not find TypeElement for " //NOI18N
1314: + runtimeExcName);
1315: return true;
1316: }
1317:
1318: Types types = compInfo.getTypes();
1319: TypeMirror runtimeExcType = runtimeExcElement.asType();
1320: for (TypeMirror exceptionType : thrownTypes) {
1321: if (!types.isSubtype(exceptionType, runtimeExcType)) {
1322: return true;
1323: }
1324: }
1325:
1326: return false;
1327: }
1328:
1329: /**
1330: */
1331: private <T extends Element> List<T> resolveHandles(
1332: CompilationInfo compInfo, List<ElementHandle<T>> handles) {
1333: if (handles == null) {
1334: return null;
1335: }
1336: if (handles.isEmpty()) {
1337: return Collections.<T> emptyList();
1338: }
1339:
1340: List<T> elements = new ArrayList<T>(handles.size());
1341: for (ElementHandle<T> handle : handles) {
1342: T element = handle.resolve(compInfo);
1343: if (element != null) {
1344: elements.add(element);
1345: } else {
1346: ErrorManager.getDefault().log(ErrorManager.WARNING,
1347: "JUnit: Could not resolve element handle " //NOI18N
1348: + handle.getBinaryName());
1349: }
1350: }
1351: return elements;
1352: }
1353:
1354: /**
1355: * Stops this creator - cancels creation of a test class.
1356: */
1357: public void cancel() {
1358: cancelled = true;
1359: }
1360:
1361: /**
1362: */
1363: private void classProcessed(ClassTree cls) {
1364: if (processedClassNames == null) {
1365: processedClassNames = new ArrayList<String>(4);
1366: }
1367: processedClassNames.add(cls.getSimpleName().toString());
1368: }
1369:
1370: /**
1371: */
1372: List<String> getProcessedClassNames() {
1373: return processedClassNames != null ? processedClassNames
1374: : Collections.<String> emptyList();
1375: }
1376:
1377: /* private methods */
1378:
1379: //XXX: retouche
1380: // /**
1381: // *
1382: // * @param cls JavaClass to generate the comment to.
1383: // */
1384: // private static void addClassBodyComment(JavaClass cls) {
1385: // int off = cls.getEndOffset() - 1;
1386: // String theComment1 = NbBundle.getMessage(TestCreator.class,
1387: // CLASS_COMMENT_LINE1);
1388: // String theComment2 = NbBundle.getMessage(TestCreator.class,
1389: // CLASS_COMMENT_LINE2);
1390: // String indent = getIndentString();
1391: // DiffElement diff = new DiffElement(
1392: // off,
1393: // off,
1394: // indent + theComment1 + '\n'
1395: // + indent + theComment2 + '\n' + '\n');
1396: // ((ResourceImpl) cls.getResource()).addExtDiff(diff);
1397: // }
1398: /**
1399: */
1400: private String getInitialMainMethodBody() {
1401: if (initialMainMethodBody == null) {
1402: initialMainMethodBody = JUnitSettings.getDefault()
1403: .getGenerateMainMethodBody();
1404: if (initialMainMethodBody == null) {
1405: /*
1406: * set it to a non-null value so that this method does not try
1407: * to load it from the settings next time
1408: */
1409: initialMainMethodBody = ""; //NOI18N
1410: }
1411: }
1412: return initialMainMethodBody;
1413: }
1414:
1415: /**
1416: */
1417: protected ModifiersTree createModifiersTree(
1418: String annotationClassName, Set<Modifier> modifiers,
1419: WorkingCopy workingCopy) {
1420: TreeMaker maker = workingCopy.getTreeMaker();
1421: AnnotationTree annotation = maker
1422: .Annotation(getClassIdentifierTree(annotationClassName,
1423: workingCopy), Collections
1424: .<ExpressionTree> emptyList());
1425: return maker.Modifiers(modifiers, Collections
1426: .<AnnotationTree> singletonList(annotation));
1427: }
1428:
1429: /** */
1430: private Map<String, ExpressionTree> classIdentifiers;
1431:
1432: /**
1433: */
1434: protected ExpressionTree getClassIdentifierTree(String className,
1435: WorkingCopy workingCopy) {
1436: ExpressionTree classIdentifier;
1437: if (classIdentifiers == null) {
1438: classIdentifier = null;
1439: classIdentifiers = new HashMap<String, ExpressionTree>(13);
1440: } else {
1441: classIdentifier = classIdentifiers.get(className);
1442: }
1443: if (classIdentifier == null) {
1444: TypeElement typeElement = getElemForClassName(className,
1445: workingCopy.getElements());
1446: TreeMaker maker = workingCopy.getTreeMaker();
1447: classIdentifier = (typeElement != null) ? maker
1448: .QualIdent(typeElement) : maker
1449: .Identifier(className);
1450: classIdentifiers.put(className, classIdentifier);
1451: }
1452: return classIdentifier;
1453: }
1454:
1455: /**
1456: */
1457: protected static TypeElement getElemForClassName(String className,
1458: Elements elements) {
1459: TypeElement elem = elements.getTypeElement(className);
1460: if (elem == null) {
1461: ErrorManager.getDefault().log(ErrorManager.ERROR,
1462: "Could not find TypeElement for " + className); //NOI18N
1463: }
1464: return elem;
1465: }
1466:
1467: /**
1468: * Creates a {@code Set} of {@code Modifier}s from the given list
1469: * of modifiers.
1470: *
1471: * @param modifiers modifiers that should be contained in the set
1472: * @return set containing exactly the given modifiers
1473: */
1474: static Set<Modifier> createModifierSet(Modifier... modifiers) {
1475: EnumSet<Modifier> modifierSet = EnumSet.noneOf(Modifier.class);
1476: for (Modifier m : modifiers) {
1477: modifierSet.add(m);
1478: }
1479: return modifierSet;
1480: }
1481:
1482: }
|