All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.testng.TestGenerator Maven / Gradle / Ivy

There is a newer version: RELEASE230
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.modules.testng;

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.WorkingCopy;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.PROTECTED;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;

/**
 *
 * @author  Marian Petras
 */
final class TestGenerator extends AbstractTestGenerator {
    
    /** */
    static final String ANN_BEFORE_CLASS = "org.testng.annotations.BeforeClass";     //NOI18N
    /** */
    static final String ANN_AFTER_CLASS = "org.testng.annotations.AfterClass";       //NOI18N
    /** */
    static final String ANN_BEFORE = "org.testng.annotations.BeforeMethod";                //NOI18N
    /** */
    static final String ANN_AFTER = "org.testng.annotations.AfterMethod";                  //NOI18N
    /** */
    static final String ANN_TEST = "org.testng.annotations.Test";                    //NOI18N
    /** */
//    private static final String ANN_RUN_WITH = "org.junit.runner.RunWith";//NOI18N
//    /** */
//    private static final String ANN_SUITE = "org.junit.runners.Suite";  //NOI18N
    /** */
    private static final String ANN_SUITE_MEMBERS = "SuiteClasses";     //NOI18N
    /** */
    private static final String BEFORE_CLASS_METHOD_NAME = "setUpClass";//NOI18N
    /** */
    private static final String AFTER_CLASS_METHOD_NAME = "tearDownClass";//NOI18N
    /** */
    private static final String BEFORE_METHOD_NAME = "setUpMethod";           //NOI18N
    /** */
    private static final String AFTER_METHOD_NAME = "tearDownMethod";         //NOI18N
    
    /**
     */
    TestGenerator(TestGeneratorSetup setup) {
        super(setup);
    }
    
    /**
     */
    TestGenerator(TestGeneratorSetup setup,
                        List> srcTopClassHandles,
                        ListsuiteMembers,
                        boolean isNewTestClass) {
        super(setup, srcTopClassHandles, suiteMembers, isNewTestClass);
    }
    
    
    /**
     */
    @Override
    protected ClassTree composeNewTestClass(WorkingCopy workingCopy,
                                            String name,
                                            List members) {
        final TreeMaker maker = workingCopy.getTreeMaker();
        ModifiersTree modifiers = maker.Modifiers(
                                      Collections.singleton(PUBLIC));
        return maker.Class(
                    modifiers,                                 //modifiers
                    name,                                      //name
                    Collections.emptyList(),//type params
                    null,                                      //extends
                    Collections.emptyList(),   //implements
                    members);                                  //members
    }
    
    /**
     */
    @Override
    protected List generateInitMembers(WorkingCopy workingCopy) {
        if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
                && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) {
            return Collections.emptyList();
        }

        List result = new ArrayList(4);
        if (setup.isGenerateBeforeClass()) {
            result.add(
                    generateInitMethod(BEFORE_CLASS_METHOD_NAME, ANN_BEFORE_CLASS, true, workingCopy));
        }
        if (setup.isGenerateAfterClass()) {
            result.add(
                    generateInitMethod(AFTER_CLASS_METHOD_NAME, ANN_AFTER_CLASS, true, workingCopy));
        }
        if (setup.isGenerateBefore()) {
            result.add(
                    generateInitMethod(BEFORE_METHOD_NAME, ANN_BEFORE, false, workingCopy));
        }
        if (setup.isGenerateAfter()) {
            result.add(
                    generateInitMethod(AFTER_METHOD_NAME, ANN_AFTER, false, workingCopy));
        }
        return result;
    }

    /**
     */
    @Override
    protected ClassTree generateMissingInitMembers(ClassTree tstClass,
                                                   TreePath tstClassTreePath,
                                                   WorkingCopy workingCopy) {
        if (!setup.isGenerateBefore() && !setup.isGenerateAfter()
                && !setup.isGenerateBeforeClass() && !setup.isGenerateAfterClass()) {
            return tstClass;
        }

        ClassMap classMap = ClassMap.forClass(tstClass, tstClassTreePath,
                                              workingCopy.getTrees());

        if ((!setup.isGenerateBefore() || classMap.containsBefore())
                && (!setup.isGenerateAfter() || classMap.containsAfter())
                && (!setup.isGenerateBeforeClass() || classMap.containsBeforeClass())
                && (!setup.isGenerateAfterClass() || classMap.containsAfterClass())) {
            return tstClass;
        }

        final TreeMaker maker = workingCopy.getTreeMaker();

        List tstMembersOrig = tstClass.getMembers();
        List tstMembers = new ArrayList(tstMembersOrig.size() + 4);
        tstMembers.addAll(tstMembersOrig);

        generateMissingInitMembers(tstMembers, classMap, workingCopy);

        ClassTree newClass = maker.Class(
                tstClass.getModifiers(),
                tstClass.getSimpleName(),
                tstClass.getTypeParameters(),
                tstClass.getExtendsClause(),
                (List) tstClass.getImplementsClause(),
                tstMembers);
        return newClass;
    }
    
    /**
     */
    @Override
    protected boolean generateMissingInitMembers(List tstMembers,
                                               ClassMap clsMap,
                                               WorkingCopy workingCopy) {
        boolean modified = false;
        
        if (setup.isGenerateBeforeClass() && !clsMap.containsBeforeClass()) {
            int targetIndex;
            if (clsMap.containsAfterClass()) {
                targetIndex = clsMap.getAfterClassIndex();
            } else {
                int beforeIndex = clsMap.getBeforeIndex();
                int afterIndex = clsMap.getAfterIndex();
                if ((beforeIndex != -1) && (afterIndex != -1)) {
                    targetIndex = Math.min(beforeIndex, afterIndex);
                } else {
                    /*
                     * if (beforeIndex != -1)
                     *     targetIndex = beforeIndex;
                     * else if (afterIndex != -1)
                     *     targetIndex = afterIndex;
                     * else
                     *     targetIndex = -1;
                     */
                    targetIndex = Math.max(beforeIndex, afterIndex);
                }
            }
            addInitMethod(BEFORE_CLASS_METHOD_NAME,
                          ANN_BEFORE_CLASS,
                          true,
                          targetIndex,
                          tstMembers,
                          clsMap,
                          workingCopy);
            modified = true;
        }
        if (setup.isGenerateAfterClass() && !clsMap.containsAfterClass()) {
            int targetIndex;
            if (clsMap.containsBeforeClass()) {
                targetIndex = clsMap.getBeforeClassIndex() + 1;
            } else {
                int beforeIndex = clsMap.getBeforeIndex();
                int afterIndex = clsMap.getAfterIndex();
                if ((beforeIndex != -1) && (afterIndex != -1)) {
                    targetIndex = Math.min(beforeIndex, afterIndex);
                } else {
                    targetIndex = Math.max(beforeIndex, afterIndex);
                }
            }
            addInitMethod(AFTER_CLASS_METHOD_NAME,
                          ANN_AFTER_CLASS,
                          true,
                          targetIndex,
                          tstMembers,
                          clsMap,
                          workingCopy);
            modified = true;
        }
        if (setup.isGenerateBefore() && !clsMap.containsBefore()) {
            int targetIndex;
            if (clsMap.containsAfter()) {
                targetIndex = clsMap.getAfterIndex();
            } else {
                int beforeClassIndex = clsMap.getBeforeClassIndex();
                int afterClassIndex = clsMap.getAfterClassIndex();
                
                /*
                 * if ((beforeClassIndex != -1) && (afterClassIndex != -1))
                 *     targetIndex = Math.max(beforeClassIndex, afterClassIndex) + 1;
                 * else if (beforeClassIndex != -1)
                 *     targetIndex = beforeClassIndex + 1;
                 * else if (afterClassIndex != -1)
                 *     targetIndex = afterClassIndex + 1;
                 * else
                 *     targetIndex = -1
                 */
                targetIndex = Math.max(beforeClassIndex, afterClassIndex);
                if (targetIndex != -1) {
                    targetIndex++;
                }
            }
            addInitMethod(BEFORE_METHOD_NAME,
                          ANN_BEFORE,
                          false,
                          targetIndex,
                          tstMembers,
                          clsMap,
                          workingCopy);
            modified = true;
        }
        if (setup.isGenerateAfter() && !clsMap.containsAfter()) {
            int targetIndex;
            if (clsMap.containsBefore()) {
                targetIndex = clsMap.getBeforeIndex() + 1;
            } else {
                int beforeClassIndex = clsMap.getBeforeClassIndex();
                int afterClassIndex = clsMap.getAfterClassIndex();
                targetIndex = Math.max(beforeClassIndex, afterClassIndex);
                if (targetIndex != -1) {
                    targetIndex++;
                }
            }
            addInitMethod(AFTER_METHOD_NAME,
                          ANN_AFTER,
                          false,
                          targetIndex,
                          tstMembers,
                          clsMap,
                          workingCopy);
            modified = true;
        }
        
        return modified;
    }

    /**
     */
    private void addInitMethod(String methodName,
                               String annotationClassName,
                               boolean isStatic,
                               int targetIndex,
                               List clsMembers,
                               ClassMap clsMap,
                               WorkingCopy workingCopy) {
        MethodTree initMethod = generateInitMethod(methodName,
                                                   annotationClassName,
                                                   isStatic,
                                                   workingCopy);
        
        if (targetIndex == -1) {
            targetIndex = getPlaceForFirstInitMethod(clsMap);
        }
        
        if (targetIndex != -1) {
            clsMembers.add(targetIndex, initMethod);
        } else {
            clsMembers.add(initMethod);
        }
        clsMap.addNoArgMethod(methodName, annotationClassName, targetIndex);
    }

    /**
     * Generates a set-up or a tear-down method.
     * The generated method will have no arguments, void return type
     * and a declaration that it may throw {@code java.lang.Exception}.
     * The method will have a declared protected member access.
     * The method contains call of the corresponding super method, i.e.
     * {@code super.setUp()} or {@code super.tearDown()}.
     *
     * @param  methodName  name of the method to be created
     * @return  created method
     */
    private MethodTree generateInitMethod(String methodName,
                                          String annotationClassName,
                                          boolean isStatic,
                                          WorkingCopy workingCopy) {
        Set methodModifiers
                = isStatic ? createModifierSet(PUBLIC, STATIC)
                           : Collections.singleton(PUBLIC);
        ModifiersTree modifiers = createModifiersTree(annotationClassName,
                                                      methodModifiers,
                                                      workingCopy);
        TreeMaker maker = workingCopy.getTreeMaker();
        BlockTree methodBody = maker.Block(
                Collections.emptyList(),
                false);
        MethodTree method = maker.Method(
                modifiers,              // modifiers
                methodName,             // name
                maker.PrimitiveType(TypeKind.VOID),         // return type
                Collections.emptyList(), // type params
                Collections.emptyList(),      // parameters
                Collections.singletonList(
                        maker.Identifier("Exception")),     // throws...//NOI18N
                methodBody,
                null);                                      // default value
        return method;
    }
    
    /**
     */
    @Override
    protected void generateMissingPostInitMethods(TreePath tstClassTreePath,
                                                  List tstMembers,
                                                  ClassMap clsMap,
                                                  WorkingCopy workingCopy) {
        /* no post-init methods */
    }
    
    /**
     */
    @Override
    protected MethodTree composeNewTestMethod(String testMethodName,
                                              BlockTree testMethodBody,
                                              List throwsList,
                                              WorkingCopy workingCopy) {
        TreeMaker maker = workingCopy.getTreeMaker();
        return maker.Method(
                createModifiersTree(ANN_TEST,
                                    createModifierSet(PUBLIC),
                                    workingCopy),
                testMethodName,
                maker.PrimitiveType(TypeKind.VOID),
                Collections.emptyList(),
                Collections.emptyList(),
                throwsList,
                testMethodBody,
                null);          //default value - used by annotations
    }
    
    /**
     */
//    @Override
//    protected ClassTree finishSuiteClass(ClassTree tstClass,
//                                         TreePath tstClassTreePath,
//                                         List tstMembers,
//                                         List suiteMembers,
//                                         boolean membersChanged,
//                                         ClassMap classMap,
//                                         WorkingCopy workingCopy) {
//
//        ModifiersTree currModifiers = tstClass.getModifiers();
//        ModifiersTree modifiers = fixSuiteClassModifiers(tstClass,
//                                                         tstClassTreePath,
//                                                         currModifiers,
//                                                         suiteMembers,
//                                                         workingCopy);
//        if (!membersChanged) {
//            if (modifiers != currModifiers) {
//                workingCopy.rewrite(currModifiers, modifiers);
//            }
//            return tstClass;
//        }
//
//        return workingCopy.getTreeMaker().Class(
//                modifiers,
//                tstClass.getSimpleName(),
//                tstClass.getTypeParameters(),
//                tstClass.getExtendsClause(),
//                (List) tstClass.getImplementsClause(),
//                tstMembers);
//    }
    
//    /**
//     * Keeps or modifies annotations and modifiers of the given suite class.
//     * Modifiers are modified such that the class is public.
//     * The list of annotations is modified such that it contains
//     * the following annotations:
//     * 
RunWith(Suite.class)
//     * @SuiteRunner.Suite({...})
// * with members of the suite in place of the {...} list. // * // * @param tstClass class whose modifiers and anntations are to be modified // * @param tstClassTreePath tree path to the class from the compilation unit // * @param modifiers current modifiers and annotations // * @param suiteMembers list of class names that should be contained // * in the test suite // * @return {@code ModifiersTree} object containing the modified set // * of class modifiers and annotations, or {@code null} // * if no modifications were necessary // */ // private ModifiersTree fixSuiteClassModifiers(ClassTree tstClass, // TreePath tstClassTreePath, // ModifiersTree modifiers, // List suiteMembers, // WorkingCopy workingCopy) { // boolean flagsModified = false; // // Set currFlags = modifiers.getFlags(); // Set flags = EnumSet.copyOf(currFlags); // flagsModified |= flags.remove(PRIVATE); // flagsModified |= flags.remove(PROTECTED); // flagsModified |= flags.add(PUBLIC); // if (!flagsModified) { // flags = currFlags; // } // // // boolean annotationListModified = false; // // List currAnnotations = modifiers.getAnnotations(); // List annotations; // if (currAnnotations.isEmpty()) { // List newAnnotations = new ArrayList(2); // newAnnotations.add(createRunWithSuiteAnnotation(workingCopy)); // newAnnotations.add(createSuiteAnnotation(suiteMembers, workingCopy)); // annotations = newAnnotations; // // annotationListModified = true; // } else { // Trees trees = workingCopy.getTrees(); // Element classElement = trees.getElement(tstClassTreePath); // List annMirrors // = classElement.getAnnotationMirrors(); // assert annMirrors.size() == currAnnotations.size(); // // // int index = -1, runWithIndex = -1, suiteClassesIndex = -1; // for (AnnotationMirror annMirror : annMirrors) { // index++; // Element annElement = annMirror.getAnnotationType().asElement(); // assert annElement instanceof TypeElement; // TypeElement annTypeElem = (TypeElement) annElement; // Name annFullName = annTypeElem.getQualifiedName(); // // if ((runWithIndex == -1) && annFullName.contentEquals(ANN_RUN_WITH)) { // runWithIndex = index; // } else if ((suiteClassesIndex == -1) // && annFullName.contentEquals(ANN_SUITE + '.' + ANN_SUITE_MEMBERS)) { // suiteClassesIndex = index; // } // } // // AnnotationTree runWithSuiteAnn; // if ((runWithIndex == -1) || !checkRunWithSuiteAnnotation( // annMirrors.get(runWithIndex), // workingCopy)) { // runWithSuiteAnn = createRunWithSuiteAnnotation(workingCopy); // } else { // runWithSuiteAnn = currAnnotations.get(runWithIndex); // } // // AnnotationTree suiteClassesAnn; // if ((suiteClassesIndex == -1) || !checkSuiteMembersAnnotation( // annMirrors.get(suiteClassesIndex), // suiteMembers, // workingCopy)) { // suiteClassesAnn = createSuiteAnnotation(suiteMembers, workingCopy); // } else { // suiteClassesAnn = currAnnotations.get(suiteClassesIndex); // } // // if ((runWithIndex != -1) && (suiteClassesIndex != -1)) { // if (runWithSuiteAnn != currAnnotations.get(runWithIndex)) { // workingCopy.rewrite( // currAnnotations.get(runWithIndex), // runWithSuiteAnn); // } // if (suiteClassesAnn != currAnnotations.get(suiteClassesIndex)) { // workingCopy.rewrite( // currAnnotations.get(suiteClassesIndex), // suiteClassesAnn); // } // annotations = currAnnotations; // } else { // List newAnnotations // = new ArrayList(currAnnotations.size() + 2); // if ((runWithIndex == -1) && (suiteClassesIndex == -1)) { // // /* // * put the @RunWith(...) and @Suite.SuiteClasses(...) // * annotations in front of other annotations // */ // newAnnotations.add(runWithSuiteAnn); // newAnnotations.add(suiteClassesAnn); // if (!currAnnotations.isEmpty()) { // newAnnotations.addAll(currAnnotations); // } // } else { // newAnnotations.addAll(currAnnotations); // if (runWithIndex == -1) { // assert suiteClassesIndex != 1; // // /* // * put the @RunWith(...) annotation // * just before the Suite.SuiteClasses(...) annotation // */ // newAnnotations.add(suiteClassesIndex, runWithSuiteAnn); // } else { // assert runWithIndex != -1; // // /* // * put the @Suite.SuiteClasses(...) annotation // * just after the @RunWith(...) annotation // */ // newAnnotations.add(runWithIndex + 1, suiteClassesAnn); // } // } // annotations = newAnnotations; // // annotationListModified = true; // } // } // // if (!flagsModified && !annotationListModified) { // return modifiers; // } // // return workingCopy.getTreeMaker().Modifiers(flags, annotations); // } // /** // * Checks that the given annotation is of type // * {@value #ANN_RUN_WITH} and contains argument // * {@value #ANN_SUITE}{@literal .class}. // * // * @param annMirror annotation to be checked // * @return {@code true} if the annotation meets the described criteria, // * {@code false} otherwise // */ // private boolean checkRunWithSuiteAnnotation(AnnotationMirror annMirror, // WorkingCopy workingCopy) { // Map annParams // = annMirror.getElementValues(); // // if (annParams.size() != 1) { // return false; // } // // AnnotationValue annValue = annParams.values().iterator().next(); // Name annValueClsName = getAnnotationValueClassName(annValue, // workingCopy.getTypes()); // return annValueClsName != null // ? annValueClsName.contentEquals(ANN_SUITE) // : false; // } /** * Checks that the given annotation is of type * {@value #ANN_SUITE}.{@value #ANN_SUITE_MEMBERS} * and contains the given list of classes as (the only) argument, * in the same order. * * @param annMirror annotation to be checked * @param suiteMembers list of fully qualified class names denoting * content of the test suite * @return {@code true} if the annotation meets the described criteria, * {@code false} otherwise */ private boolean checkSuiteMembersAnnotation(AnnotationMirror annMirror, List suiteMembers, WorkingCopy workingCopy) { Map annParams = annMirror.getElementValues(); if (annParams.size() != 1) { return false; } AnnotationValue annValue = annParams.values().iterator().next(); Object value = annValue.getValue(); if (value instanceof java.util.List) { List items = (List) value; if (items.size() != suiteMembers.size()) { return false; } Types types = workingCopy.getTypes(); Iterator suiteMembersIt = suiteMembers.iterator(); for (AnnotationValue item : items) { Name suiteMemberName = getAnnotationValueClassName(item, types); if (suiteMemberName == null) { return false; } if (!suiteMemberName.contentEquals(suiteMembersIt.next())) { return false; } } return true; } return false; } /** * Returns fully qualified class name of a class given to an annotation * as (the only) argument. * * @param annValue annotation value * @return fully qualified name of a class represented by the given * annotation value, or {@code null} if the annotation value * does not represent a class */ private Name getAnnotationValueClassName(AnnotationValue annValue, Types types) { Object value = annValue.getValue(); if (value instanceof TypeMirror) { TypeMirror typeMirror = (TypeMirror) value; Element typeElement = types.asElement(typeMirror); if (typeElement.getKind() == ElementKind.CLASS) { return ((TypeElement) typeElement).getQualifiedName(); } } return null; } // /** // * Creates annotation @org.junit.runner.RunWith. // * // * @return created annotation // */ // private AnnotationTree createRunWithSuiteAnnotation( // WorkingCopy workingCopy) { // TreeMaker maker = workingCopy.getTreeMaker(); // // /* @RunWith(Suite.class) */ // return maker.Annotation( // getClassIdentifierTree(ANN_RUN_WITH, workingCopy), // Collections.singletonList( // maker.MemberSelect( // getClassIdentifierTree(ANN_SUITE, workingCopy), // "class"))); //NOI18N // } // // /** // * Creates annotation // * @org.junit.runners.Suite.SuiteClasses({...}). // * // * @param suiteMembers fully qualified names of classes to be included // * in the test suite // * @param created annotation // */ // private AnnotationTree createSuiteAnnotation(List suiteMembers, // WorkingCopy workingCopy) { // final TreeMaker maker = workingCopy.getTreeMaker(); // // List suiteMemberExpressions // = new ArrayList(suiteMembers.size()); // for (String suiteMember : suiteMembers) { // suiteMemberExpressions.add( // maker.MemberSelect( // getClassIdentifierTree(suiteMember, workingCopy), // "class")); //NOI18N // } // // /* @Suite.SuiteClasses({TestA.class, TestB.class, ...}) */ // return maker.Annotation( // maker.MemberSelect( // getClassIdentifierTree(ANN_SUITE, workingCopy), // ANN_SUITE_MEMBERS), // Collections.singletonList( // maker.NewArray( // null, //do not generate "new Class[]" // Collections.emptyList(), // suiteMemberExpressions))); // } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy