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

org.netbeans.modules.testng.DefaultPlugin 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 java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.TypeElement;
import org.netbeans.api.actions.Savable;
import org.netbeans.api.java.classpath.ClassPath;
import static org.netbeans.api.java.classpath.ClassPath.SOURCE;
import static org.netbeans.api.java.project.JavaProjectConstants.SOURCES_TYPE_JAVA;
import org.netbeans.api.java.queries.UnitTestForSourceQuery;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.ClasspathInfo.PathKind;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.Sources;
import org.netbeans.api.queries.VisibilityQuery;
import org.netbeans.modules.gsf.testrunner.plugin.CommonPlugin.CreateTestParam;
import org.netbeans.modules.gsf.testrunner.plugin.CommonPlugin.Location;
import org.netbeans.modules.testng.TestabilityResult.SkippedClass;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;

/**
 * Default TestNG plugin.
 *
 * @author  Marian Petras
 */
public final class DefaultPlugin extends TestNGPlugin {
    
    /** name of FreeMarker template property - generate {@code @BeforeClass} method? */
    private static final String templatePropBeforeClass = "classSetUp"; //NOI18N
    /** name of FreeMarker template property - generate {@code @AfterClass} method? */
    private static final String templatePropAfterClass = "classTearDown";//NOI18N
    /** name of FreeMarker template property - generate {@code @Before} method? */
    private static final String templatePropBefore = "methodSetUp";     //NOI18N
    /** name of FreeMarker template property - generate {@code @After} method? */
    private static final String templatePropAfter = "methodTearDown";   //NOI18N
    /** name of FreeMarker template property - generate in-method source code hints? */
    private static final String templatePropCodeHints = "sourceCodeHint";   //NOI18N
    /** name of FreeMarker template property - generate hints - method placeholders? */
    private static final String templatePropMethodPH = "testMethodsPlaceholder"; //NOI18N
    /** name of FreeMarker template property - use Java annotations? */
    private static final String templatePropUseAnnotations = "useAnnotations"; //NOI18N
    /** name of FreeMarker template property - list of class names */
    private static final String templatePropClassNames = "classNames";  //NOI18N
    /**
     * name of FreeMarker template property - list of class names,
     * each with a suffix ".class"
     */
    private static final String templatePropClasses = "classes";        //NOI18N
    private static final String NGPrefix = "NG";        //NOI18N
    
    
    /**
     * 
     */
    @Override
    protected boolean canCreateTests(FileObject... fileObjects) {
        if (fileObjects.length == 0) {
            return false;
        }

        final FileObject firstFile = fileObjects[0];
        final SourceGroup sourceGroup = findSourceGroup(firstFile);
        if (sourceGroup == null) {
            return false;
        }
        final FileObject rootFolder = sourceGroup.getRootFolder();
        if (UnitTestForSourceQuery.findUnitTests(rootFolder).length == 0) {
            return false;
        }

        /*
         * Now we know that source folder of the first file has a corresponding
         * test folder (possible non-existent).
         */
        if (fileObjects.length == 1) {
            /* ... if there is just one file selected, it is all we need: */
            return true;
        }

        /*
         * ...for multiple files, we just check that all the selected files
         * have the same root folder:
         */
        for (int i = 1; i < fileObjects.length; i++) {
            FileObject fileObj = fileObjects[i];
            if (!FileUtil.isParentOf(rootFolder, fileObj)
                    || !sourceGroup.contains(fileObj)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Finds a Java source group the given file belongs to.
     * 
     * @param  file  {@code FileObject} to find a {@code SourceGroup} for
     * @return  the found {@code SourceGroup}, or {@code null} if the given
     *          file does not belong to any Java source group
     */
    private static SourceGroup findSourceGroup(FileObject file) {
        final Project project = FileOwnerQuery.getOwner(file);
        if (project == null) {
            return null;
        }

        Sources src = ProjectUtils.getSources(project);
        SourceGroup[] srcGrps = src.getSourceGroups(SOURCES_TYPE_JAVA);
        for (SourceGroup srcGrp : srcGrps) {
            FileObject rootFolder = srcGrp.getRootFolder();
            if (((file == rootFolder) || FileUtil.isParentOf(rootFolder, file)) 
                    && srcGrp.contains(file)) {
                return srcGrp;
            }
        }
        return null;
    }

    /**
     *
     */
    protected Location getTestLocation(Location sourceLocation) {
        FileObject fileObj = sourceLocation.getFileObject();
        ClassPath srcCp;
        
        if ((srcCp = ClassPath.getClassPath(fileObj, SOURCE)) == null) {
            return null;
        }
        
        String baseResName = srcCp.getResourceName(fileObj, '/', false);
        if(baseResName == null) {
            return null;
        }
        String testResName = !fileObj.isFolder()
                             ? getTestResName(baseResName, fileObj.getExt())
                             : getSuiteResName(baseResName);
        assert testResName != null;
        
        return getOppositeLocation(sourceLocation,
                                   srcCp,
                                   testResName,
                                   true);
    }
    
    /**
     *
     */
    protected Location getTestedLocation(Location testLocation) {
        FileObject fileObj = testLocation.getFileObject();
        ClassPath srcCp;
        
        if (fileObj.isFolder()
               || ((srcCp = ClassPath.getClassPath(fileObj, SOURCE)) == null)) {
            return null;
        }
        
        String baseResName = srcCp.getResourceName(fileObj, '/', false);
        if (baseResName == null) {
            return null;
        }
        String srcResName = getSrcResName(baseResName, fileObj.getExt());
        if (srcResName == null) {
            return null;     //if the selectedFO is not a test class (by name)
        }

        return getOppositeLocation(testLocation,
                                   srcCp,
                                   srcResName,
                                   false);
    }
    
    /**
     *
     */
    private static Location getOppositeLocation(
                                    final Location sourceLocation,
                                    final ClassPath fileObjCp,
                                    final String oppoResourceName,
                                    final boolean sourceToTest) {
        FileObject fileObj = sourceLocation.getFileObject();
        FileObject fileObjRoot;
        
        if ((fileObjRoot = fileObjCp.findOwnerRoot(fileObj)) == null) {
            return null;
        }
        
        URL[] oppoRootsURLs = sourceToTest
                              ? UnitTestForSourceQuery.findUnitTests(fileObjRoot)
                              : UnitTestForSourceQuery.findSources(fileObjRoot);
        //if (sourceToTest && (oppoRootsURLs.length == 0)) {
        //    PENDING - offer creation of new unit tests root
        //}
        if ((oppoRootsURLs == null) || (oppoRootsURLs.length == 0)) {
            return null;
        }
        
        ClassPath oppoRootsClassPath = ClassPathSupport
                                           .createClassPath(oppoRootsURLs);
        final List oppoFiles = oppoRootsClassPath
                                           .findAllResources(oppoResourceName);
        if (oppoFiles.isEmpty()) {
            //if (sourceToTest) {
            //    PENDING - offer creation of new test class
            //}
            return null;
        }
        
//        final ElementHandle elementHandle = sourceLocation.getElementHandle();
//        if (elementHandle == null) {
            return new Location(oppoFiles.get(0)/*, null*/);
//        }
        
//        /* Build SOURCE classpath: */
//        ClassPath[] srcCpDelegates = new ClassPath[2];
//        if (sourceToTest) {
//            srcCpDelegates[0] = fileObjCp;
//            srcCpDelegates[1] = oppoRootsClassPath;
//        } else {
//            srcCpDelegates[0] = oppoRootsClassPath;
//            srcCpDelegates[1] = fileObjCp;
//        }
//        ClassPath srcClassPath
//                = ClassPathSupport.createProxyClassPath(srcCpDelegates);
//        
//        /* Build COMPILE classpath: */
//        FileObject[] oppoRoots = oppoRootsClassPath.getRoots();
//        ClassPath[] compCpDelegates = new ClassPath[oppoRoots.length + 1];
//        int delegateIndex = 0;
//        if (sourceToTest) {
//            compCpDelegates[delegateIndex++]
//                    = ClassPath.getClassPath(fileObjRoot, COMPILE);
//        }
//        for (FileObject oppoRoot : oppoRoots) {
//            compCpDelegates[delegateIndex++]
//                    = ClassPath.getClassPath(oppoRoot, COMPILE);
//        }
//        if (!sourceToTest) {
//            compCpDelegates[delegateIndex++]
//                    = ClassPath.getClassPath(fileObjRoot, COMPILE);
//        }
//        ClassPath compClassPath
//                = ClassPathSupport.createProxyClassPath(compCpDelegates);
//        
//        /* Obtain the BOOT classpath: */
//        ClassPath bootClassPath = ClassPath.getClassPath(fileObj, BOOT);
//        
//        ClasspathInfo cpInfo = ClasspathInfo.create(bootClassPath,
//                                                    compClassPath,
//                                                    srcClassPath);
//        List files = new ArrayList(oppoFiles.size() + 1);
//        files.add(fileObj);
//        files.addAll(oppoFiles);
//        JavaSource javaSource = JavaSource.create(cpInfo, files);
//        
//        try {
//            MatchFinder matchFinder = new MatchFinder(sourceLocation,
//                                                      oppoFiles,
//                                                      sourceToTest);
//            javaSource.runUserActionTask(matchFinder, true);
//            return matchFinder.getResult();
//        } catch (IOException ex) {
//            Logger.getLogger("global").log(Level.SEVERE, null, ex);     //NOI18N
//            return null;
//        }
    }
    

    /**
     */
    private static String getTestResName(String baseResName, String ext) {
        StringBuilder buf
                = new StringBuilder(baseResName.length() + ext.length() + 10);
        buf.append(baseResName).append(NGPrefix + "Test");                         //NOI18N
        if (ext.length() != 0) {
            buf.append('.').append(ext);
        }
        return buf.toString();
    }
    
    /**
     */
    private static String getSuiteResName(String baseResName) {
        if (baseResName.length() == 0) {
            return TestNGSettings.getDefault().getRootSuiteClassName();
        }
        
        final String suiteSuffix = "Suite";                             //NOI18N

        String lastNamePart
                = baseResName.substring(baseResName.lastIndexOf('/') + 1);

        StringBuilder buf = new StringBuilder(baseResName.length()
                                              + lastNamePart.length()
                                              + suiteSuffix.length()
                                              + 6);
        buf.append(baseResName).append('/');
        buf.append(Character.toUpperCase(lastNamePart.charAt(0)))
           .append(lastNamePart.substring(1));
        buf.append(suiteSuffix);
        buf.append(".java");                                            //NOI18N

        return buf.toString();
    }
    
    /**
     */
    private static String getSrcResName(String testResName, String ext) {
        if (!testResName.endsWith(NGPrefix + "Test")) {                            //NOI18N
            return null;
        }
        
        StringBuilder buf
                = new StringBuilder(testResName.length() + ext.length());
        buf.append(testResName.substring(0, testResName.length() - 6));
        if (ext.length() != 0) {
            buf.append('.').append(ext);
        }
        return buf.toString();
    }
    
    /**
     */
    private static String getTestClassName(String baseClassName) {
        return baseClassName + NGPrefix + "Test";                                  //NOI18N
    }
    
    /**
     */
    private static String getSourceClassName(String testClassName) {
        final String suffix = "Test";                                   //NOI18N
        final int suffixLen = suffix.length();
        
        return ((testClassName.length() > suffixLen)
                    && testClassName.endsWith(suffix))
               ? testClassName.substring(0, testClassName.length() - suffixLen)
               : null;
    }
    
    /**
     */
    private static String getTestMethodName(String baseMethodName) {
        final String prefix = "test";                                   //NOI18N
        final int prefixLen = prefix.length();
        
        StringBuffer buf = new StringBuffer(prefixLen
                                            + baseMethodName.length());
        buf.append(prefix).append(baseMethodName);
        buf.setCharAt(prefixLen,
                      Character.toUpperCase(baseMethodName.charAt(0)));
        return buf.toString();
    }
    
    /**
     */
    private static String getSourceMethodName(String testMethodName) {
        final String prefix = "test";                                   //NOI18N
        final int prefixLen = prefix.length();
        
        return ((testMethodName.length() > prefixLen)
                    && testMethodName.startsWith(prefix))
               ? new StringBuilder(testMethodName.length() - prefixLen)
                        .append(Character.toLowerCase(testMethodName.charAt(prefixLen)))
                        .append(testMethodName.substring(prefixLen + 1))
                        .toString()
               : null;
    }
    
    /**
     * Creates test classes for given source classes.
     * 
     * @param filesToTest  source files for which test classes should be
     *                      created
     * @param targetRoot   root folder of the target source root
     * @param params  parameters of creating test class
     * @return created test files
     */
    @Messages({"MSG_StatusBar_CreateTest_Begin=Creating tests ...", 
         "# {0} - skipped class name",
        "# {1} - reason for skipping",
        "MSG_skipped_class=Class {0} was skipped because it was {1}.",
        "# {0} - reason for skipping the classes",
        "MSG_skipped_classes=Some classes were skipped because they were {0}.",
        "MSG_No_test_created=No tests were created because no testable class was found.",
        "PROP_testng_testClassTemplate=Templates/UnitTests/EmptyTestNGTest.java",
        "PROP_testng_testSuiteTemplate=Templates/UnitTests/TestNGSuite.xml"})
    @Override
    protected FileObject[] createTests(
                                final FileObject[] filesToTest,
                                final FileObject targetRoot,
                                final Map params) {
        //XXX: not documented that in case that if filesToTest is ,
        //the target root param works as a target folder
        
        ProgressIndicator progress = new ProgressIndicator();
        progress.show();

        String msg = Bundle.MSG_StatusBar_CreateTest_Begin();   //NOI18N
        progress.displayStatusText(msg);

        final TestCreator testCreator = new TestCreator(params);
        
        CreationResults results;
        try {
            final String templateId;
            final String suiteTemplateId;
            boolean forTestSuite = (filesToTest != null)
                    && (filesToTest.length != 0)
                    && ((filesToTest.length > 1) || !filesToTest[0].isData());
            templateId = Bundle.PROP_testng_testClassTemplate();
            suiteTemplateId = forTestSuite
                    ? Bundle.PROP_testng_testSuiteTemplate()
                    : null;
            DataObject doTestTempl = (templateId != null)
                    ? loadTestTemplate(templateId)
                    : null;
            if (doTestTempl == null) {
                return null;
            }
            DataObject doSuiteTempl = (suiteTemplateId != null)
                                      ? loadTestTemplate(suiteTemplateId)
                                      : null;
            if (forTestSuite && (doSuiteTempl == null)) {
                return null;
            }
            
            Map templateParams = createTemplateParams(params);
            setAnnotationsSupport(targetRoot, templateParams);

            if ((filesToTest == null) || (filesToTest.length == 0)) {
                //XXX: Not documented that filesToTest may be 
                
                addTemplateParamEntry(params, CreateTestParam.INC_CODE_HINT,
                                      templateParams, templatePropMethodPH);

                String testClassName = (String) params.get(CreateTestParam.CLASS_NAME);
                assert testClassName != null;
                results = new CreationResults(1);
                DataObject testDataObj = createEmptyTest(targetRoot,
                                                         testClassName,
                                                         testCreator,
                                                         templateParams,
                                                         doTestTempl);
                if (testDataObj != null) {
                    results.addCreated(testDataObj);
                }
                
            } else {
                ClassPath testClassPath = ClassPathSupport.createClassPath(
                                                new FileObject[] {targetRoot});
                if (!forTestSuite) {
                    String testClassName = (String) params.get(CreateTestParam.CLASS_NAME);
                    if (testClassName == null) {
                        String srcClassName
                                = ClassPath.getClassPath(filesToTest[0], SOURCE)
                                  .getResourceName(filesToTest[0], '.', false);
                        testClassName = getTestClassName(srcClassName);
                    }
                    try {
                        results = createSingleTest(
                                filesToTest[0],
                                testClassName,
                                testCreator,
                                templateParams,
                                doTestTempl,
                                testClassPath,
                                TestabilityResult.NO_TESTEABLE_METHODS.getReasonValue(),
                                null,              //parent suite
                                progress);
                    } catch (CreationError ex) {
                        ErrorManager.getDefault().notify(ex);
                        results = new CreationResults(1);
                    }
                } else {
                    results = new CreationResults();

                    // go through all nodes
                    for (FileObject fileToTest : filesToTest) {
                        try {
                            results.combine(createTests(fileToTest,
                                                        testCreator,
                                                        templateParams,
                                                        doTestTempl,
                                                        doSuiteTempl,
                                                        testClassPath,
                                                        null,
                                                        progress));
                        } catch (CreationError e) {
                            ErrorManager.getDefault().notify(e);
                        }
                    }
                }
            }
        } finally {
            progress.hide();
        }

        final Set skipped = results.getSkipped();
        final Set created = results.getCreated();
        if (!skipped.isEmpty()) {
            // something was skipped
            String message;
            if (skipped.size() == 1) {
                // one class? report it
                SkippedClass skippedClass = skipped.iterator().next();
                message = Bundle.MSG_skipped_class(skippedClass.clsName, strReason(skippedClass.reason, "COMMA", "AND")); //NOI18N
            } else {
                // more classes, report a general error
                // combine the results
                TestabilityResult reason = TestabilityResult.OK;
                for (SkippedClass sc : skipped) {
                    reason = TestabilityResult.combine(reason, sc.reason);
                }

                message = Bundle.MSG_skipped_classes(strReason(reason, "COMMA", "OR")); //NOI18N
            }
            TestUtil.notifyUser(message, NotifyDescriptor.INFORMATION_MESSAGE);

        }
        
        if (created.isEmpty()) {
            Mutex.EVENT.writeAccess(new Runnable() {
                public void run() {
                    TestUtil.notifyUser(
                            Bundle.MSG_No_test_created(),
                            NotifyDescriptor.INFORMATION_MESSAGE);
                }
            });
        }
        
        FileObject[] createdFiles;
        if (created.isEmpty()) {
            createdFiles = new FileObject[0];
        } else {
            createdFiles = new FileObject[created.size()];
            int i = 0;
            for (DataObject dObj : created) {
                createdFiles[i++] = dObj.getPrimaryFile();
            }
        }
        return createdFiles;
    }

    /**
     * Create a map of FreeMaker template parameters from a map
     * of {@code CreateTestParam}s.
     */
    public static final Map createTemplateParams(
                                          Map params) {
        Map result = new HashMap(7);

        addTemplateParamEntry(params, CreateTestParam.INC_CLASS_SETUP,
                              result, templatePropBeforeClass);
        addTemplateParamEntry(params, CreateTestParam.INC_CLASS_TEAR_DOWN,
                              result, templatePropAfterClass);
        addTemplateParamEntry(params, CreateTestParam.INC_SETUP,
                              result, templatePropBefore);
        addTemplateParamEntry(params, CreateTestParam.INC_TEAR_DOWN,
                              result, templatePropAfter);
        addTemplateParamEntry(params, CreateTestParam.INC_CODE_HINT,
                              result, templatePropCodeHints);

        return result;
    }

    private static void addTemplateParamEntry(Map srcParams,
                                              CreateTestParam srcParamKey,
                                              Map templParams,
                                              String templParamKey) {
        Object value = srcParams.get(srcParamKey);
        if (value instanceof Boolean) {
            templParams.put(templParamKey, Boolean.class.cast(value));
        }
    }
    
    /**
     * Creates a new test class.
     * 
     * @param  targetRoot     
     * @param  testClassName  
     * @param  testCreator  {@code TestCreator} to be used for filling
     *                      the test class template
     * @param  templateDataObj  {@code DataObject} representing
     *                          the test file template
     * @return  the created test, or {@code null} if no test was created
     */
    private DataObject createEmptyTest(FileObject targetRoot,
                                       String testClassName,
                                       TestCreator testCreator,
                                       final Map templateParams,
                                       DataObject templateDataObj) {
        if (testClassName == null) {
            throw new IllegalArgumentException("testClassName = null"); //NOI18N
        }
        
        DataObject testDataObj = null;
        try {
            DataFolder targetFolderDataObj = DataFolder.findFolder(targetRoot);
            testDataObj = templateDataObj.createFromTemplate(
                                        targetFolderDataObj,
                                        testClassName,
                                        templateParams);

            /* fill in setup etc. according to dialog settings */
            testCreator.createEmptyTest(testDataObj.getPrimaryFile());
        } catch (IOException ex) {
            ErrorManager.getDefault().notify(ex);
        }
        return testDataObj;
    }
    
    /**
     *
     */
    private static CreationResults createSingleTest(
                FileObject sourceFile,
                String requestedTestClassName,
                final TestCreator testCreator,
                final Map templateParams,
                DataObject templateDataObj,
                ClassPath testClassPath,
                long skipTestabilityResultMask,
                List parentSuite,
                ProgressIndicator progress) throws CreationError {
        
        List nonTestable;
        List> testable;
        try {
            JavaSource javaSource = JavaSource.forFileObject(sourceFile);
            //issue 161598
            if (javaSource == null)
                return CreationResults.EMPTY;
            if (skipTestabilityResultMask != 0) {
                nonTestable = new ArrayList();
                testable = TopClassFinder.findTestableTopClasses(javaSource,
                                                                 testCreator,
                                                                 nonTestable,
                                                                 skipTestabilityResultMask);
            } else {
                nonTestable = Collections.emptyList();
                testable = TopClassFinder.findTopClasses(javaSource);
            }
        } catch (IOException ex) {
            throw new CreationError(ex);
        }
        
        CreationResults result = new CreationResults(4);
        if (!nonTestable.isEmpty()) {
            result.addSkipped(nonTestable);
        }
        if (!testable.isEmpty()) {
            /* used only if (requestedTestClassName != null): */
            boolean mainClassProcessed = false;

            try {
                for (ElementHandle clsToTest : testable) {
                    String testClassName;
                    String srcClassNameFull = clsToTest.getQualifiedName();
                    if ((requestedTestClassName != null)
                            && !mainClassProcessed
                            && TestUtil.getSimpleName(srcClassNameFull)
                                   .equals(sourceFile.getName())) {
                        testClassName = requestedTestClassName;
                        mainClassProcessed = true;
                    } else {
                        testClassName = TestUtil.getTestClassName(srcClassNameFull.concat(NGPrefix));
                    }
                    String testResourceName = testClassName.replace('.', '/');

                    /* find or create the test class DataObject: */
                    DataObject testDataObj = null;
                    FileObject testFile = testClassPath.findResource(
                                            testResourceName + ".java");//NOI18N
                    boolean isNew = (testFile == null);
                    if (testFile == null) {
                        testDataObj = createTestClass(testClassPath,
                                                      testResourceName,
                                                      templateDataObj,
                                                      templateParams);
                        testFile = testDataObj.getPrimaryFile();
                    }
                    
                    testCreator.createSimpleTest(clsToTest, testFile, isNew);
                    if (testDataObj == null) {
                        testDataObj = DataObject.find(testFile);
                    }
                    save(testDataObj);
                    
                    result.addCreated(testDataObj);
                    // add the test class to the parent's suite
                    if (parentSuite != null) {
                        parentSuite.add(testClassName);
                    }
                }
            } catch (IOException ex) {       //incl. DataObjectNotFoundException
                throw new CreationError(ex);
            }
        }
        
        return result;
    }
    
    /**
     *
     */
    private static CreationResults createTests(
                final FileObject srcFileObj,
                final TestCreator testCreator,
                final Map templateParams,
                DataObject doTestT,
                DataObject doSuiteT,
                final ClassPath testClassPath,
                List parentSuite,
                ProgressIndicator progress) throws CreationError {

        CreationResults results;
        if (srcFileObj.isFolder()) {
            results = new CreationResults();

            List mySuite = new LinkedList();
            
            progress.setMessage(getScanningMsg(srcFileObj.getName()));

            for (FileObject childFileObj : srcFileObj.getChildren()) {
                if (progress.isCanceled()) {
                    results.setAbborted();
                    break;
                }
                if (!VisibilityQuery.getDefault().isVisible(childFileObj)) {
                    continue;
                }
                results.combine(createTests(childFileObj,
                                            testCreator,
                                            templateParams,
                                            doTestT,
                                            doSuiteT,
                                            testClassPath,
                                            mySuite,
                                            progress));
                if (results.isAbborted()) {
                    break;
                }
            }

            // if everything went ok, and the option is enabled,
            // create a suite for the folder .
            if (!results.isAbborted()
//                    && !mySuite.isEmpty()
                    && TestNGSettings.getDefault().isGenerateSuiteClasses()) {
                createSuiteTest(srcFileObj,
                                (String) null,
                                testCreator,
                                templateParams,
                                doSuiteT,
                                testClassPath,
                                mySuite,
                                parentSuite,
                                progress);
            }
        } else if (srcFileObj.isData() && TestUtil.isJavaFile(srcFileObj)) {
            results = createSingleTest(srcFileObj,
                                       (String) null, //use the default clsName
                                       testCreator,
                                       templateParams,
                                       doTestT,
                                       testClassPath,
                                       TestabilityResult.NO_TESTEABLE_METHODS.getReasonValue(),
                                       parentSuite,
                                       progress);
        } else {
            results = CreationResults.EMPTY;
        }
        return results;
    }

    /**
     *
     */
    private static DataObject createSuiteTest(
            FileObject folder,
            String suiteName,
            final TestCreator testCreator,
            final Map templateParams,
            DataObject templateDataObj,
            ClassPath testClassPath,
            List classesToInclude,
            List parentSuite,
            ProgressIndicator progress) throws CreationError {

        // find correct package name
        ClassPath cp = ClassPath.getClassPath(folder, SOURCE);
        assert cp != null : "SOURCE classpath was not found for " + folder; //NOI18N
        if (cp == null) {
            return null;
        }

        String pkg = cp.getResourceName(folder, '/', false);
        if (pkg.length() > 0) {
            // #176958 : append the rootFolderName in the fullSuiteName, so that we can decide in which Classpath root folder 
            //           the new Suite file will be generated, when createTestClass(...) is called later on
            String rootFolderName = ""; //NOI18N
            if (cp.getRoots().length > 1) {
                FileObject rootOwnerFO = cp.findOwnerRoot(folder);
                if (rootOwnerFO != null) {
                    rootFolderName = rootOwnerFO.getName() + '/';
                }
            }
            String dotPkg = pkg.replace('/', '.');
            String fullSuiteName = rootFolderName.concat((suiteName != null)
                    ? pkg + '/' + suiteName
                    : TestUtil.convertPackage2SuiteName(pkg));

//            String classNames = makeListOfClasses(classesToInclude, null);
//            String classes = makeListOfClasses(classesToInclude, ".class"); //NOI18N

            final Map suiteTemplParams = new HashMap(templateParams);
//            suiteTemplParams.put(templatePropClassNames, classNames);
//            suiteTemplParams.put(templatePropClasses, classes);
            String projectName = ProjectUtils.getInformation(FileOwnerQuery.getOwner(folder)).getName();
            suiteTemplParams.put("suiteName", projectName);
            suiteTemplParams.put("testName", dotPkg.concat(" suite"));
            suiteTemplParams.put("pkg", dotPkg);

            try {
                /*
                 * find or create the test class DataObject:
                 */
                DataObject testDataObj = null;
                FileObject testFile = testClassPath.findResource(
                        fullSuiteName + ".xml");       //NOI18N
                boolean isNew = (testFile == null);
                if (testFile == null) {
                    testDataObj = createTestClass(testClassPath,
                            fullSuiteName,
                            templateDataObj,
                            suiteTemplParams);
                    testFile = testDataObj.getPrimaryFile();
                }
                
                try {
                    if (testDataObj == null) {
                        testDataObj = DataObject.find(testFile);
                    }
                    save(testDataObj);
                } catch (Exception ex) {
                    ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
                    return null;
                }
                return testDataObj;
            } catch (IOException ioe) {
                throw new CreationError(ioe);
            }
        }
        return null;
    }

    /**
     *
     */
    public DataObject createSuiteTest(
                                final FileObject targetRootFolder,
                                final FileObject targetFolder,
                                final String suiteName,
                                final Map params) {
        final Map templateParams = createTemplateParams(params);
        setAnnotationsSupport(targetFolder, templateParams);
        TestCreator testCreator = new TestCreator(params);
        final ClasspathInfo cpInfo = ClasspathInfo.create(targetRootFolder);
        List testClassNames = TestUtil.getJavaFileNames(targetFolder,
                                                                cpInfo);
        
        final String templateId = Bundle.PROP_testng_testSuiteTemplate();
        
        final DataObject doSuiteTempl
                = loadTestTemplate(templateId);
        if (doSuiteTempl == null) {
            return null;
        }
        
        DataObject suiteDataObj;
        try {
            return createSuiteTest(targetFolder,
                                   suiteName,
                                   testCreator,
                                   templateParams,
                                   doSuiteTempl,
                                   cpInfo.getClassPath(PathKind.SOURCE),
                                   new LinkedList(testClassNames),
                                   null,            //parent suite
                                   null);           //progress indicator
        } catch (CreationError ex) {
            return null;
        }
    }
    
    /**
     */
    private static DataObject createTestClass(
            ClassPath cp,
            String testClassName,
            DataObject templateDataObj,
            final Map templateParams)
                                        throws DataObjectNotFoundException,
                                               IOException {
        
        FileObject root = cp.getRoots()[0];
        // #176958 : decide in which Classpath root folder the new Suite file will be generated
        String rootFolderName = ""; //NOI18N
        if (cp.getRoots().length > 1) {
            int indexFirst = testClassName.indexOf('/');
            int indexLast = testClassName.lastIndexOf('/');
            assert indexFirst != indexLast :  // this should not happen
                    "ClassPath=" + cp + "\n" + "testClassName=" + testClassName;                //NOI18N
            rootFolderName = testClassName.substring(0, indexFirst);
            testClassName = testClassName.substring(indexLast + 1);
            for (int i = 0; i < cp.getRoots().length; i++) {
                FileObject rootFO = cp.getRoots()[i];
                if (rootFO.getPath().endsWith(rootFolderName)) {
                    root = rootFO;
                }
            }
        }
        int index = testClassName.lastIndexOf('/');
        String pkg = index > -1 ? testClassName.substring(0, index)
                                : "";                                   //NOI18N
        String clazz = index > -1 ? testClassName.substring(index+1)
                                  : testClassName;

        // create package if it does not exist
        if (pkg.length() > 0) {
            root = FileUtil.createFolder(root, pkg);        //IOException
        }
        // instantiate template into the package
        return templateDataObj.createFromTemplate(          //IOException
                    DataFolder.findFolder(root),
                    clazz,
                    templateParams);
    }

    /**
     * Determines whether annotations should be used in test classes in the
     * given folder when generating the given type of TestNG tests.
     * If annotations are supported, adds this information to the map of
     * template parameters.
     * 
     * @param  testFolder  target folder for generated test classes
     * @param  templateParams  map of template params to store
     *                         the information to
     * @return  {@code true} if it was detected that annotations are supported;
     *          {@code false} otherwise
     */
    private static boolean setAnnotationsSupport(
                                        FileObject testFolder,
                                        Map templateParams) {
        if (!testFolder.isFolder()) {
            throw new IllegalArgumentException("not a folder");         //NOI18N
        }

        templateParams.put(templatePropUseAnnotations, true);
        return true;
    }

    /**
     *
     */
    private static void save(DataObject dataObj) throws IOException {
        Savable sc = dataObj.getLookup().lookup(Savable.class);
        if (null != sc) {
            sc.save();
        }
    }
        
    /**
     * Loads a test template.
     * If the template loading fails, displays an error message.
     *
     * @param  templateID  bundle key identifying the template type
     * @return  loaded template, or null if the template
     *          could not be loaded
     */
    private static DataObject loadTestTemplate(String templateID) {
        // get the Test class template
        String path = templateID;
        try {
            FileObject fo = FileUtil.getConfigFile(path);
            if (fo == null) {
                noTemplateMessage(path);
                return null;
            }
            return DataObject.find(fo);
        }
        catch (DataObjectNotFoundException e) {
            noTemplateMessage(path);
            return null;
        }
    }
        
    /**
     *
     */
    @Messages({"# {0} - template file","MSG_template_not_found=Template file {0} was not found. Check the TestNG templates in the Template manager."})
    private static void noTemplateMessage(String temp) {
        String msg = Bundle.MSG_template_not_found(temp);     //NOI18N
        NotifyDescriptor descr = new NotifyDescriptor.Message(
                msg,
                NotifyDescriptor.ERROR_MESSAGE);
        DialogDisplayer.getDefault().notify(descr);
    }

    /**
     * A helper method to create the reason string from a result
     * and two message bundle keys that indicate the separators to be used instead
     * of "," and " and " in a connected reason like: 
     * "abstract, package-private and without testable methods".
     * 

* The values of the keys are expected to be framed by two extra characters * (e.g. as in " and "), which are stripped off. These characters serve to * preserve the spaces in the properties file. * * @param reason the TestabilityResult to represent * @param commaKey bundle key for the connective to be used instead of ", " * @param andKey bundle key for the connective to be used instead of "and" * @return String composed of the reasons contained in * reason separated by the values of commaKey and * andKey. */ @Messages({"COMMA=, ","OR= or ","AND= and "}) private static String strReason(TestabilityResult reason, String commaKey, String andKey) { String strComma = Bundle.COMMA(); String strAnd = andKey.equals("OR") ? Bundle.OR() : Bundle.AND(); String strReason = reason.getReason( // string representation of the reasons strComma, strAnd); return strReason; } /** * */ @NbBundle.Messages({"# {0} - class name", "FMT_generator_status_creating=Creating: {0} ..."}) private static String getCreatingMsg(String className) { return Bundle.FMT_generator_status_creating(className); } /** * */ @NbBundle.Messages({"# {0} - source folder", "FMT_generator_status_scanning=Scanning: {0} ..."}) private static String getScanningMsg(String sourceName) { return Bundle.FMT_generator_status_scanning(sourceName); } /** * */ @NbBundle.Messages({"# {0} - source folder", "FMT_generator_status_ignoring=Ignoring: {0} ..."}) private static String getIgnoringMsg(String sourceName, String reason) { return Bundle.FMT_generator_status_ignoring(sourceName); } /** * Error thrown by failed test creation. */ @SuppressWarnings("serial") private static final class CreationError extends Exception { CreationError() {}; CreationError(Throwable cause) { super(cause); } } /** * Utility class representing the results of a test creation * process. It gatheres all tests (as DataObject) created and all * classes (as JavaClasses) for which no test was created. */ static final class CreationResults { static final CreationResults EMPTY = new CreationResults(); Set created; // Set< createdTest : DataObject > Set skipped; boolean abborted = false; CreationResults() { this(20);} CreationResults(int expectedSize) { created = new HashSet(expectedSize * 2, 0.5f); skipped = new HashSet(expectedSize * 2, 0.5f); } void setAbborted() { abborted = true; } /** * Returns true if the process of creation was abborted. The * result contains the results gathered so far. */ boolean isAbborted() { return abborted; } /** * Adds a new entry to the set of created tests. * @return true if it was added, false if it was present before */ boolean addCreated(DataObject test) { return created.add(test); } /** */ boolean addSkipped(SkippedClass skippedClass) { return skipped.add(skippedClass); } /** */ void addSkipped(Collection skippedClasses) { if (!skippedClasses.isEmpty()) { skipped.addAll(skippedClasses); } } /** * Returns a set of classes that were skipped in the process. * @return Set */ Set getSkipped() { return skipped; } /** * Returns a set of test data objects created. * @return Set */ Set getCreated() { return created; } /** * Combines two results into one. If any of the results is an * abborted result, the combination is also abborted. The * collections of created and skipped classes are unified. * @param rhs the other CreationResult to combine into this */ void combine(CreationResults rhs) { if (rhs.abborted) { this.abborted = true; } this.created.addAll(rhs.created); this.skipped.addAll(rhs.skipped); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy