com.qspin.qtaste.kernel.engine.TestEngine.java Source code

Java tutorial

Introduction

Here is the source code for com.qspin.qtaste.kernel.engine.TestEngine.java

Source

/*
Copyright 2007-2009 QSpin - www.qspin.be
    
This file is part of QTaste framework.
    
QTaste is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
QTaste 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 Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with QTaste. If not, see <http://www.gnu.org/licenses/>.
 */

package com.qspin.qtaste.kernel.engine;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.python.util.PythonInterpreter;

import com.qspin.qtaste.config.StaticConfiguration;
import com.qspin.qtaste.config.TestBedConfiguration;
import com.qspin.qtaste.config.TestEngineConfiguration;
import com.qspin.qtaste.datacollection.collection.ProbeManager;
import com.qspin.qtaste.kernel.campaign.CampaignManager;
import com.qspin.qtaste.log.Log4jServer;
import com.qspin.qtaste.reporter.testresults.TestResult;
import com.qspin.qtaste.reporter.testresults.TestResult.Status;
import com.qspin.qtaste.reporter.testresults.TestResultImpl;
import com.qspin.qtaste.reporter.testresults.TestResultsReportManager;
import com.qspin.qtaste.testsuite.TestSuite;
import com.qspin.qtaste.testsuite.impl.DirectoryTestSuite;
import com.qspin.qtaste.testsuite.impl.JythonTestScript;
import com.qspin.qtaste.util.Exec;
import com.qspin.qtaste.util.Log4jLoggerFactory;
import com.qspin.qtaste.util.versioncontrol.VersionControl;

/**
 * This is the main entry point for the TestEngine application
 *
 * @author lvboque
 */
public class TestEngine {

    protected static Logger logger = Log4jLoggerFactory.getLogger(TestEngine.class);
    private static TestSuite currentTestSuite;
    private static boolean needToRestartSUT;
    private static boolean isRestartingSUT;
    private static Exec sutStartStopExec = new Exec();
    private static volatile boolean isStartStopSUTCancellable = false;
    public static volatile boolean isStartStopSUTCancelled = false;
    private static volatile boolean ignoreControlScript = false;
    private static volatile boolean abortedByUser = false;
    private static volatile boolean isSUTStartingManually = false;
    private static volatile boolean isSUTStartedManually = false;
    private static volatile boolean isSUTRunning = false;

    static {
        // pre-load JythonTestScript class and dependencies in the background which takes several seconds
        new Thread() {
            @Override
            public void run() {
                try {
                    Class.forName(JythonTestScript.class.getName());
                } catch (Exception e) {
                    // ignore
                }
            }
        }.start();
    }

    /**
     * Check if Test was aborted by user.
     *
     * @return true if test was aborted by the user, false otherwise (aborted)
     */
    public static boolean isAbortedByUser() {
        return abortedByUser;
    }

    /**
     * Set as current Test sequence is aborted by user.
     */
    public static void setAbortedByUser() {
        logger.info("Test aborted by user");
        abortedByUser = true;
    }

    /**
     * Set Running Mode and define if was started manually
     *
     * @param startedManually define if testbed started with ignore control script or manually
     */
    private static void setSUTAsRunning(boolean startedManually) {
        isSUTStartedManually = startedManually;
        isSUTRunning = true;
    }

    /**
     * Set as Not Running
     */
    private static void setSUTAsStopped() {
        isSUTStartedManually = false;
        isSUTRunning = false;
    }

    /**
     * Return if testbed is Running
     */
    public static boolean isSUTRunning() {
        return isSUTRunning;
    }

    /**
     * Return if testbed was started manually
     */
    public static boolean isSUTStartedManually() {
        return isSUTRunning && isSUTStartedManually;
    }

    /**
     * Execute a test suite.
     *
     * @param testSuite the test suite to execute
     * @return true if execution successful, false otherwise (aborted)
     */
    public static boolean execute(TestSuite testSuite) {
        return execute(testSuite, false);
    }

    /**
     * Execute a test suite.
     *
     * @param testSuite the test suite to execute
     * @param debug true to execute in debug mode, false otherwise
     * @return true if execution successful, false otherwise (aborted)
     */
    public static boolean execute(TestSuite testSuite, boolean debug) {
        TestBedConfiguration.reloadConfigFileIfModified();
        currentTestSuite = testSuite;
        TestResultsReportManager reportManager = TestResultsReportManager.getInstance();
        if (CampaignManager.getInstance().getCurrentCampaign() != null) {
            reportManager.startReport(CampaignManager.getInstance().getTimeStampCampaign(), testSuite.getName());
        } else {
            reportManager.startReport(new Date(), testSuite.getName());
        }
        boolean executionSuccess = testSuite.execute(debug, true);
        reportManager.stopReport();
        currentTestSuite = null;
        return executionSuccess;
    }

    public static TestSuite getCurrentTestSuite() {
        return currentTestSuite;
    }

    public static boolean ignoreControlScript() {
        return ignoreControlScript;
    }

    public static void setIgnoreControlScript(boolean pIgnoreControlScript) {
        ignoreControlScript = pIgnoreControlScript;
    }

    public static boolean startSUT(TestResult tr) {
        return startSUT(tr, false);
    }

    public static boolean startSUT(TestResult tr, boolean manually) {
        isStartStopSUTCancellable = true;
        isSUTStartingManually = manually;
        boolean status = startOrStopSUT(true, tr);
        if (status) {
            isSUTStartingManually = false;
            setSUTAsRunning(ignoreControlScript);
        }
        return status;
    }

    public static boolean stopSUT(TestResult tr) {
        setSUTAsStopped();
        return startOrStopSUT(false, tr);
    }

    /**
     * Cancels start/stop of SUT. SUT stop in terminate() if is not cancellable.
     */
    public static void cancelStartStopSUT() {
        if (isStartStopSUTCancellable) {
            logger.info("Cancel start/stop SUT");
            if (isSUTStartingManually) {
                stopSUT(null);
            }
            isStartStopSUTCancelled = true;
            sutStartStopExec.kill();
        }
    }

    private static boolean startOrStopSUT(boolean start, TestResult tr) {
        needToRestartSUT = !start;
        String startOrStop = start ? "start" : "stop";
        TestBedConfiguration config = TestBedConfiguration.getInstance();
        if (hasControlScript()) {
            if (isStartStopSUTCancelled || (start && isAbortedByUser())) {
                if (tr != null) {
                    tr.setStatus(Status.FAIL);
                    tr.setExtraResultDetails("SUT " + startOrStop + " command cancelled");
                }
                return false;
            }

            // build the engine script command as a list to avoid mistakes with spaces (see ticket #7)
            String scriptFilename = config.getControlScriptFileName();
            List<String> scriptEngineCommand = new ArrayList<>();

            if (scriptFilename.endsWith(".py")) {
                final String qtasteJar = StaticConfiguration.QTASTE_ROOT
                        + "/kernel/target/qtaste-kernel-deploy.jar";
                final String jythonLib = StaticConfiguration.JYTHON_LIB;
                final String additionalJythonLib = StaticConfiguration.ADDITIONAL_JYTHON_LIB.trim();
                final String classPath = System.getProperties().getProperty("java.class.path", "").trim();

                scriptEngineCommand.add("java");
                scriptEngineCommand.add("-Dpython.path=" + qtasteJar + File.pathSeparator + jythonLib
                        + (additionalJythonLib.isEmpty() ? "" : File.pathSeparator + additionalJythonLib));
                scriptEngineCommand.add("-cp");
                scriptEngineCommand.add(qtasteJar + File.pathSeparator + classPath);
                scriptEngineCommand.add("org.python.util.jython");
            }

            // then, build the 'start or stop' command as a list
            List<String> startOrStopCommand = new ArrayList<>();

            startOrStopCommand.add(scriptFilename);
            startOrStopCommand.add(startOrStop);

            String scriptArguments = config.getControlScriptArguments();
            if (scriptArguments != null) {
                startOrStopCommand.add(scriptArguments);
            }

            if (isRestartingSUT) {
                startOrStopCommand.add("-restart true");
            }

            logger.info((start ? "Starting" : "Stopping") + " SUT using command '"
                    + StringUtils.join(startOrStopCommand, " ") + "'");

            // report the control script
            try {
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                Map<String, String> env = new HashMap<>(System.getenv());
                env.put("TESTBED", config.getFileName());

                // build the full command to execute
                List<String> startOrStopFullCommand = new ArrayList<>();
                startOrStopFullCommand.addAll(scriptEngineCommand);
                startOrStopFullCommand.addAll(startOrStopCommand);

                logger.trace("FULL COMMAND : '" + StringUtils.join(startOrStopFullCommand, " ") + "'");

                // execute the full command
                int exitCode = sutStartStopExec.exec(
                        startOrStopFullCommand.toArray(new String[startOrStopFullCommand.size()]), env, output);

                if (isStartStopSUTCancelled || (start && isAbortedByUser())) {
                    String errMsg = "SUT " + startOrStop + " command cancelled";
                    logger.info(errMsg);
                    if (tr != null) {
                        tr.setStatus(Status.FAIL);
                        tr.setExtraResultDetails(errMsg);
                    }
                    return false;
                } else if (exitCode == 0) {
                    logger.info("SUT " + (start ? "started" : "stopped"));
                    return true;
                } else {
                    String errMsg = "SUT " + startOrStop + " command '" + startOrStopCommand
                            + "' exited with error code " + exitCode + ". Output:\n" + output.toString();
                    logger.error(errMsg);
                    if (tr != null) {
                        tr.setExtraResultDetails(errMsg);
                    }
                }
            } catch (IOException e) {
                String errMsg = "Couldn't execute SUT " + startOrStop + " command '" + startOrStopCommand + "': "
                        + e.getMessage();
                logger.error(errMsg);
                if (tr != null) {
                    tr.setExtraResultDetails(errMsg);
                }
            } catch (InterruptedException e) {
                String errMsg = "Interrupted while executing SUT " + startOrStop + " command '" + startOrStopCommand
                        + "': " + e.getMessage();
                logger.error(errMsg);
                if (tr != null) {
                    tr.setExtraResultDetails(errMsg);
                }
            }
            logger.error("Couldn't " + startOrStop + " SUT");
            if (tr != null) {
                tr.setStatus(Status.FAIL);
            }
        } else {
            logger.info("No SUT control script available for this testbed!");
        }
        return false;
    }

    public static boolean restartSUT() {
        if (useControlScript()) {
            logger.info("Restarting SUT");
            TestResult tr = new TestResultImpl("Restart SUT", null, null, 1, 1);
            tr.setTestScriptVersion("-");
            tr.start();
            TestResultsReportManager reportManager = TestResultsReportManager.getInstance();
            reportManager.putEntry(tr);
            isRestartingSUT = true;
            boolean returnValue = stopSUT(tr) && startSUT(tr);
            isRestartingSUT = false;
            tr.stop();
            reportManager.refresh();
            // reportManager.stopReport();
            return returnValue;
        } else {
            return false;
        }
    }

    public static boolean needToRestartSUT() {
        return useControlScript() && needToRestartSUT;
    }

    /**
     * Set need to restart SUT.
     *
     * @return value of needToRestartSUT, which may be set to false if no
     * control_script declared
     */
    public static boolean setNeedToRestartSUT() {
        needToRestartSUT = useControlScript();
        return needToRestartSUT;
    }

    /**
     * Initialize Test Engine by starting the test bed
     */
    public static boolean initialize() {
        if (useControlScript()) {
            isStartStopSUTCancellable = true;
            isStartStopSUTCancelled = false;
            boolean success = true;

            TestResult tr = new TestResultImpl("Start SUT", null, null, 1, 1);
            tr.setTestScriptVersion("-");
            tr.start();
            TestResultsReportManager reportManager = TestResultsReportManager.getInstance();
            reportManager.putEntry(tr);
            success &= stopSUT(tr);
            if (success) {
                success &= startSUT(tr);
            }
            tr.stop();
            reportManager.refresh();
            if (!success) {
                return false;
            }
        }

        logger.info("Starting TestEngine");
        ProbeManager.getInstance().start();
        return true;
    }

    /**
     * Terminate Test Engine by stopping all Probes and Test bed.
     */
    public static void terminate() {
        // Stop all the probes
        ProbeManager.getInstance().stop();

        isStartStopSUTCancellable = false;
        isStartStopSUTCancelled = false;

        if (useControlScript()) {
            TestResultsReportManager reportManager = TestResultsReportManager.getInstance();
            TestResult tr = new TestResultImpl("Stop SUT", null, null, 1, 1);
            tr.setTestScriptVersion("-");
            tr.start();
            reportManager.putEntry(tr);
            stopSUT(tr);
            tr.stop();
            reportManager.refresh();
        }

        logger.info("TestEngine terminated");
    }

    /**
     * Reset Test Engine configuration and close any opened probe.
     */
    public static void tearDown() {
        abortedByUser = false;
        isStartStopSUTCancellable = false;
        isStartStopSUTCancelled = false;
    }

    public static void shutdown() {
        Log4jServer.getInstance().shutdown();
        LogManager.shutdown();
    }

    private static boolean hasControlScript() {
        return TestBedConfiguration.getInstance().hasControlScript();
    }

    private static boolean useControlScript() {
        return hasControlScript() && !ignoreControlScript();
    }

    private static void showUsage() {
        System.err.println(
                "Usage: <command> -testsuite <testsuiteDirectory> -testbed <configFileName.xml> [-engine <engineFileName.xml>] "
                        + "[-loop [<count> | <hours>h]] [-sutversion <sut_version_identifier>]");
        shutdown();
        System.exit(1);
    }

    public static void main(String[] args) {
        boolean executionResult = false;
        try {
            // Log4j Configuration
            PropertyConfigurator.configure(StaticConfiguration.CONFIG_DIRECTORY + "/log4j.properties");

            // log version information
            logger.info("QTaste kernel version: " + com.qspin.qtaste.kernel.Version.getInstance().getFullVersion());
            logger.info("QTaste testAPI version: " + VersionControl.getInstance().getTestApiVersion(""));

            // handle optional config file name
            if ((args.length < 4) || (args.length > 10)) {
                showUsage();
            }
            String testSuiteDir = null;
            String testbed = null;
            int numberLoops = 1;
            boolean loopsInHours = false;
            int i = 0;
            while (i < args.length) {
                if (args[i].equals("-testsuite") && (i + 1 < args.length)) {
                    logger.info("Using " + args[i + 1] + " as test suite directory");
                    testSuiteDir = args[i + 1];
                    i += 2;
                } else if (args[i].equals("-testbed") && (i + 1 < args.length)) {
                    logger.info("Using " + args[i + 1] + " as testbed configuration file");
                    testbed = args[i + 1];
                    i += 2;
                } else if (args[i].equals("-engine") && (i + 1 < args.length)) {
                    logger.info("Using " + args[i + 1] + " as engine configuration file");
                    TestEngineConfiguration.setConfigFile(args[i + 1]);
                    i += 2;
                } else if (args[i].equals("-loop")) {
                    String message = "Running test suite in loop";
                    numberLoops = -1;
                    if ((i + 1 < args.length)) {
                        // more arguments, check if next argument is a loop argument
                        if (args[i + 1].startsWith("-")) {
                            i++;
                        } else {
                            String countOrHoursStr;
                            if (args[i + 1].endsWith("h")) {
                                loopsInHours = true;
                                countOrHoursStr = args[i + 1].substring(0, args[i + 1].length() - 1);
                            } else {
                                loopsInHours = false;
                                countOrHoursStr = args[i + 1];
                            }
                            try {
                                numberLoops = Integer.parseInt(countOrHoursStr);
                                if (numberLoops <= 0) {
                                    throw new NumberFormatException();
                                }
                                message += (loopsInHours ? " during " : " ") + numberLoops + " "
                                        + (loopsInHours ? "hour" : "time") + (numberLoops > 1 ? "s" : "");
                                i += 2;
                            } catch (NumberFormatException e) {
                                showUsage();
                            }
                        }
                    } else {
                        // no more arguments
                        i++;
                    }
                    logger.info(message);
                } else if (args[i].equals("-sutversion") && (i + 1 < args.length)) {
                    logger.info("Using " + args[i + 1] + " as sutversion");
                    TestBedConfiguration.setSUTVersion(args[i + 1]);
                    i += 2;
                } else {
                    showUsage();
                }
            }

            if (testSuiteDir == null || testbed == null) {
                showUsage();
            }

            TestBedConfiguration.setConfigFile(testbed);

            // start the log4j server
            Log4jServer.getInstance().start();

            // initialize Python interpreter
            Properties properties = new Properties();
            properties.setProperty("python.home", StaticConfiguration.JYTHON_HOME);
            properties.setProperty("python.path", StaticConfiguration.JYTHON_LIB);
            PythonInterpreter.initialize(System.getProperties(), properties, new String[] { "" });
            TestSuite testSuite = DirectoryTestSuite.createDirectoryTestSuite(testSuiteDir);
            testSuite.setExecutionLoops(numberLoops, loopsInHours);
            executionResult = execute(testSuite);
        } finally {
            shutdown();
        }
        System.exit(executionResult ? 0 : 1);
    }
}