org.paxml.testng.PaxmlTestCaseFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.paxml.testng.PaxmlTestCaseFactory.java

Source

/**
 * This file is part of PaxmlTestNG.
 *
 * PaxmlTestNG is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PaxmlTestNG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PaxmlTestNG.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.paxml.testng;

import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import javassist.Modifier;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.paxml.core.Context;
import org.paxml.launch.LaunchModel;
import org.paxml.launch.LaunchPoint;
import org.paxml.launch.Matcher;
import org.paxml.launch.Paxml;
import org.paxml.tag.AbstractTag;
import org.paxml.testng.AbstractPaxmlTestResult.ResultType;
import org.testng.annotations.Factory;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;

/**
 * paxml test case factory for TestNG.
 * 
 * @author Xuetao Niu
 * 
 */
public class PaxmlTestCaseFactory {
    private static final AtomicInteger SEQUENCE = new AtomicInteger(0);
    /**
     * The paxml launch plan file path.
     */
    public static final String PARAM_NAME_PLANFILE = "paxmlTestPlanFile";
    public static final String PARAM_NAME_SUPPRESS_GROUPS = "paxmlSuppressGroups";
    public static final String PARAM_NAME_RESULT_DIR = "paxmlTestResultDir";
    public static final String PARAM_NAME_RESULT_TYPE = "paxmlTestResultType";

    private static final Log log = LogFactory.getLog(PaxmlTestCaseFactory.class);
    private static final Object LOCK = new Object();

    // this hashmap is accessed only from synchronization block, so no need to
    // have extra synchronization nor be concurrent hash map.
    private static final Map<String, Constructor<? extends PaxmlTestCase>> CACHE = new HashMap<String, Constructor<? extends PaxmlTestCase>>();

    public static interface ILockedOperation<T> {
        T perform();
    }

    /**
     * The factory method.
     * 
     * @param planFile
     *            the plan file
     * @param suppressedGroups
     *            the execution groups to suppress
     * @param outputDir
     *            the dir to output results
     * @param resultType
     *            the format of results
     * @return the test objects.
     */
    @Factory
    @Parameters({ PARAM_NAME_PLANFILE, PARAM_NAME_SUPPRESS_GROUPS, PARAM_NAME_RESULT_DIR, PARAM_NAME_RESULT_TYPE })
    public Object[] create(final String planFile, @Optional("") final String suppressedGroups,
            @Optional("./target/surefire-reports/paxml/results") final String outputDir,
            @Optional("JSON") final String resultType) {
        final List<Matcher> suppression = new ArrayList<Matcher>(0);
        for (String groupName : AbstractTag.parseDelimitedString(suppressedGroups, null)) {
            Matcher matcher = new Matcher();
            matcher.setMatchPath(false);
            matcher.setPattern(groupName);
            suppression.add(matcher);
        }
        return performLocked(new ILockedOperation<Object[]>() {

            @Override
            public Object[] perform() {

                File resultFolder = null;
                ResultType rt = null;
                final long start = System.currentTimeMillis();
                long pid = -1;
                try {
                    File dir = new File(outputDir);
                    rt = ResultType.valueOf(resultType.toUpperCase());
                    resultFolder = new File(dir, SEQUENCE.getAndIncrement() + "/");
                    TestCases r = createTestCases(planFile, suppression.isEmpty() ? null : suppression,
                            resultFolder, rt);
                    pid = r.planPid;
                    PaxmlTestCase.init(r.objects.length, start, FilenameUtils.getBaseName(planFile));

                    // clean the paxml thread context and log into the default
                    // file
                    Context.cleanCurrentThreadContext();
                    if (log.isInfoEnabled()) {
                        log.info("Launching totally " + r.objects.length + " tests ...");
                    }

                    return r.objects;
                } catch (Throwable e) {
                    if (log.isErrorEnabled()) {
                        log.error("Cannot create test cases", e);
                    }
                    return new Object[] {
                            new PaxmlPlanFileFailure(e, planFile, resultFolder, rt, Context.getCurrentContext(),
                                    Thread.currentThread().getName(), pid, start, System.currentTimeMillis()) };
                }
            }

        });
    }

    /**
     * Let the shared context be propagated to other threads.
     * 
     * @param <T>
     * @param op
     * @return
     */
    public static <T> T performLocked(ILockedOperation<T> op) {
        synchronized (LOCK) {
            return op.perform();
        }
    }

    private class TestCases {
        Object[] objects;
        long planPid;
    }

    private TestCases createTestCases(String planFile, List<Matcher> suppression, File outputDir,
            ResultType resultType) {

        LaunchModel model = Paxml.executePlanFile(planFile, null);

        List<LaunchPoint> points = model.getLaunchPoints(false, -1);

        List<Object> result = new LinkedList<Object>();
        for (LaunchPoint p : points) {
            if (isSuppressed(suppression, p.getGroup())) {
                if (log.isInfoEnabled()) {
                    log.info("This scenario '" + p.getResource().getName()
                            + "' will not run because its group is suppressed: " + p.getGroup());
                }
            } else {
                result.add(createTestCase(p, outputDir, resultType));
            }

        }
        if (result.isEmpty()) {

            if (log.isWarnEnabled()) {
                log.warn("No scenarios will run from plan file:" + planFile);
            }
        }
        TestCases r = new TestCases();
        r.objects = result.toArray(new Object[result.size()]);
        r.planPid = model.getPlanProcessId();
        return r;
    }

    private static Object createTestCase(LaunchPoint p, File outputDir, ResultType resultType) {
        String className = p.getResource().getName();

        if (StringUtils.isNoneBlank(p.getGroup())) {
            className = p.getGroup() + "." + className;
        }

        try {
            Constructor<? extends PaxmlTestCase> constructor = CACHE.get(className);
            if (constructor == null) {
                ClassPool pool = ClassPool.getDefault();
                if (log.isInfoEnabled()) {
                    log.info("Generating test class proxy:" + className);
                }
                CtClass testclass = pool.makeClass(className);
                final CtClass superClass = pool.get(PaxmlTestCase.class.getName());
                testclass.setSuperclass(superClass);
                testclass.setModifiers(Modifier.PUBLIC);

                // Add a constructor which will call super( ... );
                CtClass[] params = new CtClass[] { pool.get(LaunchPoint.class.getName()),
                        pool.get(File.class.getName()), pool.get(ResultType.class.getName()) };
                final CtConstructor ctor = CtNewConstructor.make(params, null, CtNewConstructor.PASS_PARAMS, null,
                        null, testclass);
                testclass.addConstructor(ctor);

                Class<? extends PaxmlTestCase> c = testclass.toClass();
                constructor = c.getConstructor(new Class[] { LaunchPoint.class, File.class, ResultType.class });
                CACHE.put(className, constructor);
            }
            return constructor.newInstance(new Object[] { p, outputDir, resultType });

        } catch (Exception e) {
            throw new RuntimeException("Could not create test case: " + className, e);
        }
    }

    private boolean isSuppressed(List<Matcher> suppression, String groupName) {
        if (suppression == null || suppression.isEmpty()) {
            return false;
        }
        for (Matcher m : suppression) {
            if (m.match(groupName)) {
                return true;
            }
        }
        return false;
    }
}